/**
 * Auth entity types and client-safe schemas.
 *
 * Defines the runtime types for the fuz identity system:
 * `Account`, `Actor`, `RoleGrant`, `AuthSession`, and `ApiToken`.
 *
 * Identifier primitives (`Username`, `UsernameProvided`, `Email`) live
 * in `primitive_schemas.ts` — they're general validator shapes that
 * don't depend on the auth domain. The auth-shape request-contract
 * primitive `ActingActor` lives in `http/auth_shape.ts` next to
 * `RouteAuth` (the two pair: `auth.actor !== 'none'` ⟺ input declares
 * `acting?: ActingActor`).
 *
 * DDL lives in `auth/auth_ddl.ts`; role system in `auth/role_schema.ts`.
 * See docs/identity.md for design rationale.
 *
 * @module
 */
import { z } from 'zod';
import { Uuid } from '@fuzdev/fuz_util/id.js';
import { Username, Email } from '../primitive_schemas.js';
/**
 * Maximum length of the optional free-form `revoked_reason` attached to a
 * revoked role_grant. Bounds the value at the schema layer so both the admin
 * input (when the route surfaces a reason field) and the revokee-facing
 * `role_grant_revoke` WS notification validate against the same ceiling.
 */
export const ROLE_GRANT_REVOKED_REASON_LENGTH_MAX = 500;
export const is_role_grant_active = (p, now = new Date()) => !p.revoked_at && (!p.expires_at || new Date(p.expires_at) > now);
// Client-safe Zod schemas — for route output validation and ActionSpec outputs.
/** Zod schema for `SessionAccount` — account without sensitive fields. */
export const SessionAccountJson = z.strictObject({
    id: Uuid,
    username: Username,
    email: Email.nullable(),
    email_verified: z.boolean(),
    created_at: z.string(),
});
/** Zod schema for `AuthSession` — id is the blake3 hash, safe for client. */
export const AuthSessionJson = z.strictObject({
    id: z.string(),
    account_id: Uuid,
    created_at: z.string(),
    expires_at: z.string(),
    last_seen_at: z.string(),
});
/** Zod schema for client-safe API token listing (excludes `token_hash`). */
export const ClientApiTokenJson = z.strictObject({
    id: z.string(),
    account_id: Uuid,
    name: z.string(),
    expires_at: z.string().nullable(),
    last_used_at: z.string().nullable(),
    last_used_ip: z.string().nullable(),
    created_at: z.string(),
});
/** Zod schema for the role_grant summary returned in admin account listings. */
export const RoleGrantSummaryJson = z.strictObject({
    id: Uuid,
    role: z.string(),
    scope_kind: z.string().nullable(),
    scope_id: Uuid.nullable(),
    created_at: z.string(),
    expires_at: z.string().nullable(),
    granted_by: Uuid.nullable(),
});
/** Zod schema for the actor summary returned in admin account listings. */
export const ActorSummaryJson = z.strictObject({
    id: Uuid,
    name: z.string(),
});
/** Zod schema for admin-facing account data — extends `SessionAccountJson` with audit fields. */
export const AdminAccountJson = SessionAccountJson.extend({
    updated_at: z.string(),
    updated_by: Uuid.nullable(),
    /**
     * Soft-delete tombstone, non-null when the account is deleted. Surfaced
     * so the admin UI can mark tombstoned rows (shown only when the listing
     * is requested with `include_deleted`) and offer reactivation via
     * `account_undelete`. Active listings always carry `null` here.
     */
    deleted_at: z.string().nullable(),
});
/**
 * Zod schema for a pending role_grant offer surfaced in admin account listings.
 *
 * Deliberately narrower than `RoleGrantOfferJson`: omits `message` and
 * `decline_reason` so cross-admin visibility of the listing does not expose
 * grantor-authored text that the audit log also withholds. Full offer
 * payloads remain available through the offer-specific RPC surface and the
 * audit log when admins need them.
 *
 * `from_username` is resolved server-side so multi-admin deployments can see
 * at a glance whose pending offer is blocking a "+ {role}" button; the
 * resolution runs inside the listing query's parallel batch.
 */
export const PendingOfferSummaryJson = z.strictObject({
    id: Uuid,
    role: z.string(),
    scope_kind: z.string().nullable(),
    scope_id: Uuid.nullable(),
    from_actor_id: Uuid,
    from_username: z.string(),
    created_at: z.string(),
    expires_at: z.string(),
});
/** Zod schema for an admin account listing entry (account + actor + role_grants + pending offers). */
export const AdminAccountEntryJson = z.strictObject({
    account: AdminAccountJson,
    actor: ActorSummaryJson.nullable(),
    role_grants: z.array(RoleGrantSummaryJson),
    pending_offers: z.array(PendingOfferSummaryJson),
});
/**
 * Convert an `Account` to a `SessionAccount` by stripping sensitive fields.
 *
 * @param account - the full account record
 * @returns the client-safe account
 */
export const to_session_account = (account) => ({
    id: account.id,
    username: account.username,
    email: account.email,
    email_verified: account.email_verified,
    created_at: account.created_at,
});
/**
 * Convert an `Account` to an `AdminAccountJson` for admin listings.
 *
 * @param account - the full account record
 * @returns the admin-safe account with audit fields
 */
export const to_admin_account = (account) => ({
    ...to_session_account(account),
    updated_at: account.updated_at,
    updated_by: account.updated_by,
    deleted_at: account.deleted_at,
});
