import type { Principal } from 'shared-auth';
import type { AuthenticatedUser } from '../auth/auth.service';

/**
 * Module augmentation: extends `express-session`'s `SessionData`
 * with the BFF-specific fields we persist on the session.
 *
 * Today: just `user` — the identity resolved by `/auth/callback`.
 * Later (per ADR-0010 §"Session payload"): the encrypted `tokens`
 * blob, `audience`, `createdAt`, `absoluteExpiresAt`, etc. Each new
 * field lands as a new entry in this interface so consumers get
 * compile-time visibility.
 *
 * The file is imported (for its side-effect) by `session.module.ts`
 * so the augmentation is always in the TypeScript compile graph —
 * `req.session.user` would otherwise be `unknown` at every call
 * site.
 */
declare module 'express-session' {
  interface SessionData {
    user?: AuthenticatedUser;
    /**
     * Authorization principal per
     * [ADR-0025 §"Principal shape"](../../../../docs/decisions/0025-authorization-model-privileges-roles-scopes.md).
     * Built once at sign-in by `PrincipalBuilder` from the three
     * source claims (Entra `roles` for privileges, Entra `groups`
     * for functional roles, portal `user_scopes` for scopes) and
     * read by the upcoming `@RequirePrivilege` / `@RequireRole` /
     * `@RequireScope` guards on every request.
     *
     * Kept alongside `user` rather than replacing it: the audit
     * module and the AI-bridge controller still key on
     * `user.oid` / `user.tid` directly and would be churn-only
     * to migrate. New consumers should reach for `principal`.
     *
     * Optional in the type — sessions persisted before this field
     * existed will have it undefined. Guards must treat the
     * missing-principal case the same as "no privileges, no roles,
     * no scopes" rather than crashing.
     */
    principal?: Principal;
    /**
     * Epoch ms at which the BFF created this session. Posed by the
     * callback at the same time as `user`. Lets the absolute-timeout
     * middleware compute "is this session past its 12 h hard
     * ceiling?" without re-reading the timeout env on every request.
     */
    createdAt?: number;
    /**
     * Epoch ms at which the session is hard-expired regardless of
     * activity (per ADR-0010 §"TTL policy"). Checked on every
     * request by the absolute-timeout middleware. Independent from
     * the idle TTL on the Redis key — whichever fires first ends
     * the session.
     */
    absoluteExpiresAt?: number;
    /**
     * Double-submit CSRF token (per ADR-0009). 32 random bytes
     * base64url-encoded; minted at session creation alongside
     * `user` and lasts the session lifetime. The same value is
     * mirrored to the `__Host-portal_csrf` cookie (HttpOnly: false
     * so the SPA can read it) and compared with the `X-CSRF-Token`
     * request header by the CSRF middleware. The cookie is the
     * SPA's read-only mirror; the source of truth lives here.
     */
    csrfToken?: string;
    /**
     * Epoch ms of the last MFA assertion captured on this session.
     * Set at sign-in by the callback (Entra's CA policy is the
     * authority on whether MFA actually occurred — the BFF just
     * stamps "now" when it persists a session whose `amr` reflects
     * MFA). Will be refreshed by future step-up re-auth flows per
     * ADR-0011.
     *
     * Consumed by `RequireMfaGuard` to decide whether the session
     * is "fresh enough" for routes decorated with `@RequireMfa()`.
     * Undefined when the session was created before this field
     * existed — the guard treats undefined as "no recent MFA
     * assertion" and triggers step-up.
     */
    mfaVerifiedAt?: number;
  }
}

// The empty export turns this file into a module, which is what
// declaration merging on `express-session` requires.
export {};
