import { maintenanceGuard, type MaintenanceConfig } from '@platform-modules/util/maintenance';

/** The themed 503 page the middleware rewrites to. Exempt from the gate (else infinite loop). */
export const MAINTENANCE_RENDER_PATH = '/maintenance';

// Operator escape + loop break: the admin area (login + the toggle live here), the API (admin app +
// login POST), and the render target are ALWAYS reachable during maintenance. /admin and /api are
// already auth-gated, so exempting them never exposes anything; it only guarantees an operator can
// log in and turn maintenance off. Match on a path SEGMENT boundary so "/posts/admin-guide" is NOT
// treated as the admin area.
const EXEMPT_PREFIXES = ['/admin', '/api'];

export function isMaintenanceExempt(pathname: string): boolean {
  if (pathname === MAINTENANCE_RENDER_PATH) return true;
  return EXEMPT_PREFIXES.some((p) => pathname === p || pathname.startsWith(`${p}/`));
}

export interface MaintenanceDecision {
  blocked: boolean;
  retryAfterSec?: number;
}

/**
 * Pure gate decision: exempt paths pass; otherwise defer to the module's `maintenanceGuard`
 * (operator floor + array-coercion live there). Surfaces retryAfterSec for the 503 header.
 */
export function evaluateMaintenance(
  cfg: MaintenanceConfig,
  roles: string[] | null,
  pathname: string,
): MaintenanceDecision {
  if (!cfg.enabled) return { blocked: false };
  if (isMaintenanceExempt(pathname)) return { blocked: false };
  const verdict = maintenanceGuard(cfg, roles);
  if (!verdict.blocked) return { blocked: false };
  const retry = verdict.headers?.['Retry-After'];
  return { blocked: true, retryAfterSec: retry ? Number(retry) : undefined };
}

/** Minimal slice of the Astro middleware context the gate needs (keeps this file astro-import-free). */
export interface MaintenanceGateContext {
  url: URL;
  rewrite: (path: string) => Promise<Response>;
}

/** Injected loaders — thunks so config/role resolution is SKIPPED on the exempt/disabled fast paths. */
export interface MaintenanceGateDeps {
  loadConfig: () => Promise<MaintenanceConfig>;
  resolveRoles: () => Promise<string[] | null>;
}

/**
 * The full gate: returns a themed 503 Response when the request must be blocked, else null
 * (caller proceeds with next()). Exempt paths short-circuit BEFORE loadConfig (no DB read);
 * roles resolve only when enabled. On block, rewrites to the themed render target and re-wraps
 * it as 503 + Retry-After + noindex, copying any Set-Cookie the page set. (Session-refresh cookies
 * rotated onto context.cookies during resolveRoles are applied to this Response by Astro.)
 */
export async function runMaintenanceGate(
  ctx: MaintenanceGateContext,
  deps: MaintenanceGateDeps,
): Promise<Response | null> {
  const pathname = ctx.url.pathname;
  if (isMaintenanceExempt(pathname)) return null;
  const cfg = await deps.loadConfig();
  if (!cfg.enabled) return null;
  const roles = await deps.resolveRoles();
  const decision = evaluateMaintenance(cfg, roles, pathname);
  if (!decision.blocked) return null;

  const rendered = await ctx.rewrite(MAINTENANCE_RENDER_PATH);
  const headers = new Headers();
  const ct = rendered.headers.get('content-type');
  if (ct) headers.set('content-type', ct);
  const setCookie = rendered.headers.get('set-cookie');
  if (setCookie) headers.set('set-cookie', setCookie);
  headers.set('Retry-After', String(decision.retryAfterSec ?? 3600));
  headers.set('X-Robots-Tag', 'noindex');
  return new Response(rendered.body, { status: 503, headers });
}
