/**
 * Domain types for OAuth2 protocol flows in `@app/o-auth/flows/`. Transport-agnostic
 * input + response shapes consumed by the composite handlers (create-token, get-jwks, …)
 * and produced by the variant-specific services. Aggregates over all flows so future
 * additions (revoke-token, introspect-token, …) declare their own input/response types
 * here instead of fragmenting across multiple files.
 *
 * The types are LIVE business contracts — Hexagonal `domain/` layer — so they live next
 * to the domain errors and depend on nothing framework-specific.
 */

/**
 * RFC 6749 §5.1 — Successful Response payload returned by POST /api/o-auth/token.
 * `expires_in` is the access token lifetime in seconds from the moment of issuance.
 */
export interface OAuthCreateTokenResponse {
  access_token: string;
  token_type: 'Bearer';
  expires_in: number;
  refresh_token: string;
}

/** Inputs the password-grant sub-handler receives (after the composite handler strips the discriminator). */
export interface OAuthCreateTokenPasswordInput {
  clientId: string;
  clientSecret: string;
  username: string;
  password: string;
  ipAddress: string | null;
  userAgent: string | null;
}

/** Inputs the refresh-grant sub-handler receives (after the composite handler strips the discriminator). */
export interface OAuthCreateTokenRefreshInput {
  clientId: string;
  clientSecret: string;
  refreshToken: string;
  ipAddress: string | null;
  userAgent: string | null;
}

/** Inputs the authorization-code grant sub-service receives (after the composite handler strips the discriminator). */
export interface OAuthCreateTokenAuthorizationCodeInput {
  /** The authorization code issued by `GET /api/o-auth/authorize`. */
  code: string;
  /**
   * Public client identifier — the `OAuthApplication.code` value (not a UUID).
   * Used to resolve the application + technical `OAuthClient` for this grant.
   */
  clientId: string;
  /** Client secret for confidential clients (HTTP Basic authentication). */
  clientSecret: string;
  /** Must exactly match the `redirect_uri` recorded when the code was issued. */
  redirectUri: string;
  /** PKCE code verifier — raw random string whose S256 hash must match the stored code_challenge. */
  codeVerifier: string;
  ipAddress: string | null;
  userAgent: string | null;
}

/**
 * Canonical input accepted by the composite `OAuthCreateTokenHandler`. Both transports
 * (REST controller + GraphQL resolver) parse their on-wire shape into THIS shape, with
 * `grantType` already normalized to the canonical enum, and call `handler.main(input)`.
 *
 * The handler owns:
 *   - Client-credential validation (RFC 6749 §2.3.1: missing client → `invalid_client`).
 *   - Grant-required field validation (RFC 6749 §5.2: missing `username`/`password` for
 *     PASSWORD, missing `refreshToken` for REFRESH_TOKEN → `invalid_request`).
 *   - Unsupported grant dispatch (RFC 6749 §5.2: anything other than the supported set
 *     → `unsupported_grant_type`).
 *
 * Transports MUST NOT validate any of the above. They just extract fields from their
 * on-wire format and pass them as-is. If a field is absent or malformed, they pass
 * `undefined`/`''` and let the handler reject it — that keeps the RFC 6749 §5.2 policy
 * in ONE place, regardless of transport.
 *
 * `grantType: 'UNSUPPORTED'` is the transport's signal that the wire-format grant string
 * could not be mapped to a known canonical value. The handler rejects it with
 * `unsupported_grant_type`.
 */
export interface OAuthCreateTokenInput {
  grantType: 'PASSWORD' | 'REFRESH_TOKEN' | 'AUTHORIZATION_CODE' | 'UNSUPPORTED';
  /** Raw on-wire grant string when `grantType === 'UNSUPPORTED'`; useful for telemetry. */
  rawGrantType?: string;
  clientId: string;
  clientSecret: string;
  /** Required when `grantType === 'PASSWORD'`. */
  username?: string;
  /** Required when `grantType === 'PASSWORD'`. */
  password?: string;
  /** Required when `grantType === 'REFRESH_TOKEN'`. */
  refreshToken?: string;
  /** Required when `grantType === 'AUTHORIZATION_CODE'`. The authorization code from the redirect. */
  code?: string;
  /** Required when `grantType === 'AUTHORIZATION_CODE'`. Must match the redirect_uri bound to the code. */
  redirectUri?: string;
  /** Required when `grantType === 'AUTHORIZATION_CODE'`. PKCE code verifier for S256 verification. */
  codeVerifier?: string;
  ipAddress: string | null;
  userAgent: string | null;
}

/**
 * Input accepted by `OAuthAuthorizeHandler` for the Authorization Code flow entry point
 * (`GET /api/o-auth/authorize`). Carries the RFC 6749 §4.1.1 + RFC 7636 PKCE query parameters,
 * plus the accountId extracted from the hub_session cookie (null when no session is present).
 */
export interface OAuthAuthorizeInput {
  /** Application code (OAuthApplication.code) — public client_id in the OAuth sense. */
  clientId: string;
  /** Must exactly match OAuthClient.redirect (D5 — exact match only). */
  redirectUri: string;
  /** Must be 'code' — only response_type=code is supported. */
  responseType: string;
  /** Non-empty opaque value echoed back on redirect; validated by the satellite (D5). */
  state: string;
  /** Space-separated scope string (cosmetic per D6). May be undefined. */
  scope?: string;
  /** PKCE code challenge (RFC 7636 §4.2). Required; absent value triggers invalid_request. */
  codeChallenge: string;
  /** Must be 'S256'. 'plain' and absent both trigger invalid_request (D3). */
  codeChallengeMethod: string;
  /** Account ID extracted from the verified hub_session cookie. null = no session. */
  accountId: string | null;
  /** Full original URL of the /authorize request (used to build the continue= redirect). */
  authorizeUrl: string;
}

/** Discriminated result returned by `OAuthAuthorizeHandler`. */
export type OAuthAuthorizeResult =
  | OAuthAuthorizeCodeResult
  | OAuthAuthorizeRedirectResult;

/**
 * Session is present and valid — code has been minted and the handler has computed
 * the redirect URL that carries `code` + `state`. The controller issues 302.
 */
export interface OAuthAuthorizeCodeResult {
  kind: 'code';
  /** Full redirect URL: `${redirect_uri}?code=<code>&state=<state>`. */
  redirectUrl: string;
}

/**
 * Session is absent — the handler has computed the sign-in redirect URL.
 * The controller issues 302 to `{frontend}/sign-in?continue=<authorizeUrl>`.
 */
export interface OAuthAuthorizeRedirectResult {
  kind: 'redirect';
  /** Full sign-in page URL: `${frontendUrl}/sign-in?continue=<encoded-authorize-url>`. */
  signInUrl: string;
}

/**
 * Canonical input accepted by the composite `OAuthRevokeTokenHandler` (RFC 7009 token
 * revocation). Both transports (REST `POST /api/o-auth/revoke` + GraphQL `oAuthRevokeToken`)
 * parse their on-wire shape into THIS shape and call `handler.main(input)`.
 *
 * The handler owns the cheap presence checks — `invalid_client` on missing client
 * credentials (RFC 7009 §2.1 / RFC 6749 §2.3.1), `invalid_request` on a missing `token`. The
 * service authenticates the client against the store and revokes the token. Unknown,
 * malformed, and already-revoked tokens collapse to a successful no-op (RFC 7009 §2.2) so the
 * endpoint never reveals whether a token existed.
 */
export interface OAuthRevokeTokenInput {
  clientId: string;
  clientSecret: string;
  /** The token to revoke. For this provider the revocable credential is the refresh token. */
  token: string;
  /** RFC 7009 §2.1 hint; accepted but not required — the service treats `token` as a refresh token. */
  tokenTypeHint?: string;
}

/**
 * RFC 7009 §2.2 — the revocation endpoint always answers success. `revoked` is therefore
 * always `true`; it exists so the transports have a concrete value to map (REST 200, GraphQL
 * `true`) and never leaks whether the presented token actually existed.
 */
export interface OAuthRevokeTokenResponse {
  revoked: boolean;
}
