import type { CookieOptions } from 'express';

/**
 * Name of the persistent post-auth session cookie (per ADR-0009 §
 * "Cookies" and ADR-0010). Carries the opaque session id signed by
 * `SESSION_SECRET`; the payload itself lives in Redis.
 *
 * Production uses the `__Host-` prefix, which browsers only accept
 * when the cookie is set with `Secure`, `Path=/`, and no `Domain`
 * attribute — a hard binding that defeats a class of cross-site
 * cookie-injection attacks. Local dev runs on HTTP, where `Secure`
 * isn't possible, so the prefix is dropped to keep the dev server
 * usable. The two are toggled by `NODE_ENV`, same convention as
 * {@link preAuthCookieName}.
 */
const PRODUCTION_NAME = '__Host-portal_session';
const DEVELOPMENT_NAME = 'portal_session';

export function sessionCookieName(): string {
  return process.env['NODE_ENV'] === 'production' ? PRODUCTION_NAME : DEVELOPMENT_NAME;
}

/**
 * Defaults from ADR-0010 §"TTL policy":
 *   idle (sliding): 30 min — cookie `maxAge`, refreshed on each
 *     request by `express-session` with `rolling: true`.
 *   absolute: 12 h — checked separately in a future interceptor;
 *     not enforced by the cookie itself.
 *
 * Both overridable via env so staging / prod can tighten without a
 * code change.
 */
const DEFAULT_IDLE_TIMEOUT_SECONDS = 1800;
const DEFAULT_ABSOLUTE_TIMEOUT_SECONDS = 43200;

export interface SessionTimeouts {
  readonly idleSeconds: number;
  readonly absoluteSeconds: number;
}

export function readSessionTimeouts(): SessionTimeouts {
  return {
    idleSeconds: parsePositiveInt(
      process.env['SESSION_IDLE_TIMEOUT_SECONDS'],
      DEFAULT_IDLE_TIMEOUT_SECONDS,
      'SESSION_IDLE_TIMEOUT_SECONDS',
    ),
    absoluteSeconds: parsePositiveInt(
      process.env['SESSION_ABSOLUTE_TIMEOUT_SECONDS'],
      DEFAULT_ABSOLUTE_TIMEOUT_SECONDS,
      'SESSION_ABSOLUTE_TIMEOUT_SECONDS',
    ),
  };
}

export function sessionCookieOptions(idleSeconds: number): CookieOptions {
  const isProduction = process.env['NODE_ENV'] === 'production';
  return {
    httpOnly: true,
    sameSite: 'lax',
    secure: isProduction,
    path: '/',
    maxAge: idleSeconds * 1000,
  };
}

function parsePositiveInt(raw: string | undefined, fallback: number, name: string): number {
  if (raw === undefined || raw === '') {
    return fallback;
  }
  const value = Number(raw);
  if (!Number.isInteger(value) || value <= 0) {
    throw new Error(`${name} must be a positive integer (got "${raw}").`);
  }
  return value;
}
