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

/** Decoded contents of a session JWT, once verified. */
export interface AuthTokenPayload {
  /** UserProfile.id this token authenticates. */
  userProfileId: number;
  /** Snapshot of UserProfile.tokenVersion at sign time — checked against the current DB value on every request (see requireAuth) to allow server-side invalidation. */
  tokenVersion: number;
}

/**
 * Signs a new session JWT for the given profile, expiring per
 * `JWT_EXPIRES_IN`. The resulting string is what gets set as the session
 * cookie's value.
 */
export function signAuthToken(payload: AuthTokenPayload): string {
  // "sub" follows the JWT convention (RFC 7519) of identifying the
  // principal as a string; userProfileId/tokenVersion are our own claims.
  return jwt.sign(
    { sub: String(payload.userProfileId), tokenVersion: payload.tokenVersion },
    env.JWT_SECRET,
    { expiresIn: env.JWT_EXPIRES_IN as jwt.SignOptions["expiresIn"] },
  );
}

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