import jwt from "jsonwebtoken";
import { env } from "../config/env.js";

/**
 * Decoded contents of an **admin** session JWT, once verified. Deliberately
 * a separate token type from `lib/jwt.ts`'s `AuthTokenPayload`: the admin
 * app authenticates against its own `AdminUser` table with its own secret
 * (`ADMIN_JWT_SECRET`), so an end-user session token and an admin session
 * token are never interchangeable.
 */
export interface AdminTokenPayload {
  /** `AdminUser.id` this token authenticates. */
  adminUserId: number;
  /** Snapshot of `AdminUser.tokenVersion` at sign time — re-checked against the DB on every request (see `requireAdmin`) to allow server-side invalidation. */
  tokenVersion: number;
}

/**
 * The admin JWT secret, or a thrown error if the instance never configured
 * one. A misconfigured admin deployment surfaces as a loud 500 on login
 * rather than a silently-unsigned token; a deployment that doesn't run the
 * admin app at all never reaches here (nothing calls sign/verify), and
 * `requireAdmin` independently fails closed on the same unset value.
 */
function adminSecret(): string {
  if (env.ADMIN_JWT_SECRET === undefined) {
    throw new Error("ADMIN_JWT_SECRET is not configured — cannot issue or verify admin sessions");
  }
  return env.ADMIN_JWT_SECRET;
}

/** Signs a new admin session JWT, expiring per `JWT_EXPIRES_IN` (shared with the end-user token — same "how long a session lasts" policy). */
export function signAdminToken(payload: AdminTokenPayload): string {
  return jwt.sign(
    { sub: String(payload.adminUserId), tokenVersion: payload.tokenVersion },
    adminSecret(),
    { expiresIn: env.JWT_EXPIRES_IN as jwt.SignOptions["expiresIn"] },
  );
}

/**
 * Verifies an admin session JWT's signature/expiry and decodes it back
 * into an {@link AdminTokenPayload}.
 *
 * @throws {Error} if the token is invalid/expired (from `jwt.verify`) or
 *   structurally malformed (missing/wrong-typed claims).
 */
export function verifyAdminToken(token: string): AdminTokenPayload {
  const decoded = jwt.verify(token, adminSecret());
  const adminUserId = typeof decoded === "object" ? Number(decoded.sub) : Number.NaN;
  if (
    typeof decoded !== "object" ||
    Number.isNaN(adminUserId) ||
    typeof decoded.tokenVersion !== "number"
  ) {
    throw new Error("Malformed admin token payload");
  }
  return { adminUserId, tokenVersion: decoded.tokenVersion };
}
