syntax = "proto3";

package shrikedb.identity.v2;

// Identity management service for ShrikeDB.
//
// This service handles tenant provisioning, user management, role-based access
// control, and API key lifecycle. It is only available when the server is built
// with `--features multi-tenant`.
//
// Authentication: Registered identity-service requests require a valid
// credential via `authorization: Bearer <JWT>` or `x-shrikedb-api-key: <key>`
// (legacy `x-fusiondb-api-key` is accepted). Only users with
// PermissionMode::Admin or PermissionMode::Grant may call admin-level RPCs.
service IdentityService {
  // ── Tenant management ──────────────────────────────────────────────────────
  rpc CreateTenant(CreateTenantRequest) returns (CreateTenantResponse);
  rpc GetTenant(GetTenantRequest) returns (GetTenantResponse);
  rpc ListTenants(ListTenantsRequest) returns (ListTenantsResponse);
  rpc SuspendTenant(SuspendTenantRequest) returns (SuspendTenantResponse);

  // ── User management ────────────────────────────────────────────────────────
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc UpdateUserStatus(UpdateUserStatusRequest) returns (UpdateUserStatusResponse);

  // ── Role management ────────────────────────────────────────────────────────
  rpc CreateRole(CreateRoleRequest) returns (CreateRoleResponse);
  rpc GetRole(GetRoleRequest) returns (GetRoleResponse);

  // ── Authorization policy management ───────────────────────────────────────
  // Policy documents are JSON-encoded shrikedb-security PolicyDocument values.
  // They are persisted in the identity registry and applied to the hot policy
  // cache immediately after SetPolicyDocument succeeds.
  rpc SetPolicyDocument(SetPolicyDocumentRequest) returns (SetPolicyDocumentResponse);
  rpc GetPolicyDocument(GetPolicyDocumentRequest) returns (GetPolicyDocumentResponse);

  // ── API key management ─────────────────────────────────────────────────────
  // CreateApiKey returns the raw_key exactly once. Callers MUST store it
  // securely; it cannot be retrieved after this call.
  rpc CreateApiKey(CreateApiKeyRequest) returns (CreateApiKeyResponse);
  rpc RevokeApiKey(RevokeApiKeyRequest) returns (RevokeApiKeyResponse);

  // ── Authentication ─────────────────────────────────────────────────────────
  // Login with username + password. Returns a JWT access token (15 min) and
  // refresh token (7 days). Does NOT require prior authentication.
  rpc Login(LoginRequest) returns (LoginResponse);
  rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);

  // ── Introspection ──────────────────────────────────────────────────────────
  // WhoAmI returns the TenantContext for the current request's credential.
  rpc WhoAmI(WhoAmIRequest) returns (WhoAmIResponse);
}

// ── Shared primitives ──────────────────────────────────────────────────────────

message UidProto {
  uint64 id = 1;
}

enum PermissionScopeProto {
  PERMISSION_SCOPE_UNSPECIFIED = 0;
  PERMISSION_SCOPE_TENANT = 1;
  PERMISSION_SCOPE_KV_NAMESPACE = 2;
  PERMISSION_SCOPE_GRAPH_NAMESPACE = 3;
  PERMISSION_SCOPE_VECTOR_INDEX = 4;
  PERMISSION_SCOPE_SEMANTIC_PROFILE = 5;
  PERMISSION_SCOPE_ONTOLOGY_VIEW = 6;
}

enum PermissionModeProto {
  PERMISSION_MODE_UNSPECIFIED = 0;
  PERMISSION_MODE_READ = 1;
  PERMISSION_MODE_WRITE = 2;
  PERMISSION_MODE_DELETE = 3;
  PERMISSION_MODE_SCHEMA = 4;
  PERMISSION_MODE_GRANT = 5;
  PERMISSION_MODE_ADMIN = 6;
}

message PermissionProto {
  PermissionScopeProto scope = 1;
  PermissionModeProto mode = 2;
  // Namespace name (for KV/Graph/Vector scopes). Empty for Tenant scope.
  string namespace = 3;
}

// ── Tenant messages ────────────────────────────────────────────────────────────

message CreateTenantRequest {
  string slug = 1;
  string display_name = 2;
}

message CreateTenantResponse {
  UidProto tenant_uid = 1;
  string slug = 2;
}

message GetTenantRequest {
  UidProto tenant_uid = 1;
}

message GetTenantResponse {
  UidProto tenant_uid = 1;
  string slug = 2;
  string display_name = 3;
  bool suspended = 4;
  string created_at = 5;  // RFC 3339
}

message ListTenantsRequest {
  // 1-200; zero selects the default of 50.
  uint32 page_size = 1;
  // Opaque token returned by the previous page.
  string page_token = 2;
}

message ListTenantsResponse {
  repeated GetTenantResponse tenants = 1;
  string next_page_token = 2;
}

message SuspendTenantRequest {
  UidProto tenant_uid = 1;
  bool suspend = 2;  // true = suspend, false = reactivate
}

message SuspendTenantResponse {
  bool suspended = 1;
}

// ── User messages ──────────────────────────────────────────────────────────────

message CreateUserRequest {
  UidProto tenant_uid = 1;
  string username = 2;
  // password is optional: omit for API-key-only users
  string password = 3;
  // role_uids to assign at creation time
  repeated UidProto role_uids = 4;
}

message CreateUserResponse {
  UidProto user_uid = 1;
  string username = 2;
}

message GetUserRequest {
  UidProto user_uid = 1;
}

message GetUserResponse {
  UidProto user_uid = 1;
  UidProto tenant_uid = 2;
  string username = 3;
  string status = 4;       // "active" | "suspended" | "pending_verification"
  string created_at = 5;   // RFC 3339
  repeated UidProto role_uids = 6;
}

message UpdateUserStatusRequest {
  UidProto user_uid = 1;
  string status = 2;  // "active" | "suspended"
}

message UpdateUserStatusResponse {
  string status = 1;
}

// ── Role messages ──────────────────────────────────────────────────────────────

message CreateRoleRequest {
  UidProto tenant_uid = 1;
  string name = 2;
  repeated PermissionProto permissions = 3;
}

message CreateRoleResponse {
  UidProto role_uid = 1;
  string name = 2;
}

message GetRoleRequest {
  UidProto role_uid = 1;
}

message GetRoleResponse {
  UidProto role_uid = 1;
  UidProto tenant_uid = 2;
  string name = 3;
  repeated PermissionProto permissions = 4;
  string created_at = 5;  // RFC 3339
}

// ── Authorization policy messages ─────────────────────────────────────────────

message SetPolicyDocumentRequest {
  UidProto tenant_uid = 1;
  string policy_json = 2;
}

message SetPolicyDocumentResponse {
  bool applied = 1;
}

message GetPolicyDocumentRequest {
  UidProto tenant_uid = 1;
}

message GetPolicyDocumentResponse {
  string policy_json = 1;
}

// ── API key messages ───────────────────────────────────────────────────────────

message CreateApiKeyRequest {
  UidProto user_uid = 1;
  string name = 2;
}

message CreateApiKeyResponse {
  UidProto key_uid = 1;
  string key_prefix = 2;
  // raw_key is shown ONCE. The server does not store this.
  // Format: fdb_{kid_hex}_{secret_hex}
  string raw_key = 3;
}

message RevokeApiKeyRequest {
  UidProto key_uid = 1;
}

message RevokeApiKeyResponse {
  bool revoked = 1;
}

// ── Auth messages ──────────────────────────────────────────────────────────────

message LoginRequest {
  string username = 1;
  string password = 2;
  // Optional tenant qualifier. Omit only for bootstrap/root compatibility or
  // when the username is globally unique.
  UidProto tenant_uid = 3;
}

message LoginResponse {
  string access_token = 1;   // JWT, 15 min
  string refresh_token = 2;  // JWT, 7 days
  uint64 expires_in = 3;     // access token TTL in seconds
}

message RefreshTokenRequest {
  string refresh_token = 1;
}

message RefreshTokenResponse {
  string access_token = 1;
  uint64 expires_in = 2;
  // Rotated opaque refresh token. Store this value and discard the old one.
  string refresh_token = 3;
}

// ── Introspection ──────────────────────────────────────────────────────────────

message WhoAmIRequest {}

message WhoAmIResponse {
  UidProto user_uid = 1;
  UidProto tenant_uid = 2;
  string auth_method = 3;   // "api_key" | "jwt" | "system"
  repeated PermissionProto permissions = 4;
}
