// MeService -- the caller-bound surface for self-management of sessions
// and API keys -- plus the admin-side ResolveApiKey used by the auth
// middleware to map an inbound API key to its owning Identity.
//
// Milestone A (Login / Logout / GetMe) and milestone B (IssueMyKey /
// ListMyKeys / RevokeMyKey / ResolveApiKey) RPCs land here. Sessions
// listing, JWK registration, and the broader IdentityService admin
// surface (BootstrapIdentity, GetInternalIdentity, ListIdentities) are
// deferred to milestone C.
syntax = "proto3";

package tars.identity.v1;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "tars/auth/v1/auth_options.proto";
import "tars/cli/v1/cli.proto";
import "tars/common/v1/types.proto";
import "tars/docs/v1/docs.proto";
import "tars/identity/v1/api_key.proto";
import "tars/identity/v1/identity.proto";

option go_package = "github.com/tetrateio/fraser/api/tars/identity/v1;identityv1";

// MeService is the caller-bound surface -- every RPC operates on the
// authenticated identity, with no path-level identity selector. The
// CLI's `ia whoami`, `ia keys ...`, `ia login` consume this.
service MeService {
  // Login initiates an OIDC authorization-code + PKCE flow. Returns an
  // authorization_url the caller must redirect the user's browser to.
  // After the IdP authenticates and redirects to redirect_uri, the
  // platform establishes a session cookie for subsequent requests.
  rpc Login(LoginRequest) returns (LoginResponse) {
    option (tars.docs.v1.title) = "Initiate OIDC login";
    option (tars.docs.v1.group) = "Account";
    option (google.api.http) = {
      post: "/v1/auth/login"
      body: "*"
    };
    // CLI: the `ia login` command is hand-rolled because the PKCE +
    // loopback orchestration is not a regular unary RPC. Hide the
    // generated leaf to avoid two competing entries in --help.
    option (tars.cli.v1.command) = {hidden: true};
  }

  // Logout invalidates the caller's current OIDC session. Subsequent
  // requests using the session cookie return 401 unauthenticated. Does
  // not revoke API keys the identity may have issued.
  rpc Logout(LogoutRequest) returns (LogoutResponse) {
    option (tars.docs.v1.title) = "Log out";
    option (tars.docs.v1.group) = "Account";
    option (google.api.http) = {
      post: "/v1/auth/logout"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_USER_TOKEN]
    };
    option (tars.cli.v1.command) = {
      path: ["logout"]
    };
  }

  // GetMe returns the full identity record for the authenticated caller.
  // Use to verify which identity a credential belongs to, populate a
  // "logged in as" UI element, or confirm role assignments before making
  // permission-gated calls.
  rpc GetMe(GetMeRequest) returns (GetMeResponse) {
    option (tars.docs.v1.title) = "Get current identity";
    option (tars.docs.v1.group) = "Account";
    option (google.api.http) = {get: "/v1/me"};
    option idempotency_level = NO_SIDE_EFFECTS;
    option (tars.auth.v1.auth) = {
      auth_types: [
        AUTH_TYPE_USER_TOKEN,
        AUTH_TYPE_API_KEY
      ]
    };
    option (tars.cli.v1.command) = {
      path: ["whoami"]
    };
  }

  // IssueMyKey issues a new long-lived API key for the caller. The
  // secret field in the response is returned exactly once and cannot be
  // retrieved again. Store it in a secrets manager immediately.
  rpc IssueMyKey(IssueMyKeyRequest) returns (IssueMyKeyResponse) {
    option (tars.docs.v1.title) = "Issue an API key";
    option (tars.docs.v1.group) = "API Keys";
    option (google.api.http) = {
      post: "/v1/me/keys"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_USER_TOKEN]
    };
    option (tars.cli.v1.command) = {
      path: [
        "keys",
        "create"
      ]
    };
  }

  // ListMyKeys lists all API keys belonging to the caller. Returns
  // metadata (id, name, creation timestamp, last-used timestamp) but
  // never the plaintext secret.
  rpc ListMyKeys(ListMyKeysRequest) returns (ListMyKeysResponse) {
    option (tars.docs.v1.title) = "List API keys";
    option (tars.docs.v1.group) = "API Keys";
    option (google.api.http) = {get: "/v1/me/keys"};
    option idempotency_level = NO_SIDE_EFFECTS;
    option (tars.auth.v1.auth) = {
      auth_types: [
        AUTH_TYPE_USER_TOKEN,
        AUTH_TYPE_API_KEY
      ]
    };
    option (tars.cli.v1.command) = {
      path: [
        "keys",
        "list"
      ]
    };
  }

  // RevokeMyKey permanently and irrevocably revokes an API key.
  // Revocation propagates to all active data planes within sub-second;
  // requests already in flight that passed authentication before
  // propagation may still complete.
  rpc RevokeMyKey(RevokeMyKeyRequest) returns (RevokeMyKeyResponse) {
    option (tars.docs.v1.title) = "Revoke an API key";
    option (tars.docs.v1.group) = "API Keys";
    option (google.api.http) = {delete: "/v1/me/keys/{key_id}"};
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_USER_TOKEN]
    };
    option (tars.cli.v1.command) = {
      path: [
        "keys",
        "revoke"
      ]
      primary_flag: "key_id"
    };
  }
}

// IdentityService carries the admin-side endpoints that the auth
// middleware and onboarding paths need. ResolveApiKey is the only RPC
// landed in milestones A+B; the rest of the admin surface ships in
// milestone C.
service IdentityService {
  // ResolveApiKey maps an API-key plaintext to its owning Identity.
  // Internal-only -- only callable over the mTLS-protected internal
  // subdomain. The auth middleware calls this on every inbound request
  // that presents Authorization: Bearer <api_key>.
  rpc ResolveApiKey(ResolveApiKeyRequest) returns (ResolveApiKeyResponse) {
    option idempotency_level = NO_SIDE_EFFECTS;
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {hidden: true};
  }
}

// --- MeService request/response messages ---

message LoginRequest {
  // Operator's OIDC issuer alias (allows multi-IdP operators to select).
  // Empty selects the default issuer for the operator domain.
  string issuer = 1;

  // Where the IdP should redirect after authentication. For the CLI this
  // is a localhost loopback URL bound to a free port.
  string redirect_uri = 2 [(google.api.field_behavior) = REQUIRED];

  // PKCE code challenge generated by the client.
  string code_challenge = 3 [(google.api.field_behavior) = REQUIRED];

  // PKCE code-challenge method. Only S256 is accepted.
  string code_challenge_method = 4 [(google.api.field_behavior) = REQUIRED];
}

message LoginResponse {
  // URL the client follows to the upstream IdP.
  string authorization_url = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Opaque state value bound to the request. Echoed by the IdP and
  // verified by the /v1/auth/callback handler.
  string state = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message LogoutRequest {}

message LogoutResponse {}

message GetMeRequest {}

message GetMeResponse {
  // Full identity record for the authenticated caller.
  Identity identity = 1;
}

message IssueMyKeyRequest {
  // Human-friendly label for the key. Surfaced in `ia keys list` output.
  string name = 1 [(google.api.field_behavior) = REQUIRED];

  // What the key is authorized to do. Unspecified defaults to PROMPT.
  ApiKeyType type = 2;

  // Optional caller-supplied idempotency key.
  string idempotency_key = 99;
}

message IssueMyKeyResponse {
  // Metadata for the newly issued key (id, name, prefix, type, state).
  ApiKey key = 1;

  // Plaintext API key value. Returned exactly once at issue time.
  string secret = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message ListMyKeysRequest {
  // Pagination cursor and page size for the listing.
  tars.common.v1.PageRequest page = 1;
}

message ListMyKeysResponse {
  // The caller's API keys (metadata only; never the plaintext secret).
  repeated ApiKey keys = 1;

  // Pagination state; carries the next-page cursor when more remain.
  tars.common.v1.PageResponse page = 2;
}

message RevokeMyKeyRequest {
  // Id (key_<ulid>) of the caller-owned key to revoke.
  string key_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message RevokeMyKeyResponse {}

// --- IdentityService request/response messages ---

message ResolveApiKeyRequest {
  // Plaintext API key value (sent over mTLS only).
  string key = 1 [(google.api.field_behavior) = REQUIRED];
}

message ResolveApiKeyResponse {
  // Identity that owns the resolved key.
  Identity identity = 1;

  // Metadata for the resolved key (id, type, state, owning identity).
  ApiKey key = 2;
}
