/**
 * `@platform-modules/util/maintenance` — maintenance-mode guard (pure, zero-dep, web-standard).
 *
 * A pure verdict function (NOT a framework middleware): the host wires the result into its own
 * `Request`→`Response` (Astro / Hono / Worker). No framework coupling (§5 host-agnostic rule).
 *
 * HARD FLOOR — operator escape is non-negotiable: `admin`/`owner` are ALWAYS admitted. The
 * effective allow-set is `(cfg.allowRoles ?? []) ∪ ['admin','owner']`; `cfg.allowRoles` only ADDS
 * roles, it can never REMOVE the operator floor — even `allowRoles:[]` must not lock the operator out.
 */

export interface MaintenanceConfig {
  enabled: boolean
  message?: string
  allowRoles?: string[]
  retryAfterSec?: number
}

export interface MaintenanceVerdict {
  blocked: boolean
  status?: 503
  headers?: Record<string, string>
  message?: string
}

/** The operator floor — always part of the effective allow-set, never removable by config. */
const OPERATOR_ROLES = ['admin', 'owner'] as const
const DEFAULT_RETRY_AFTER_SEC = 3600

export function maintenanceGuard(
  cfg: MaintenanceConfig,
  principalRoles: string[] | null,
): MaintenanceVerdict {
  if (!cfg.enabled) return { blocked: false }

  // Coerce both inputs to arrays before use. Despite the `string[]` types, a JS host
  // can hand a malformed shape across the trust boundary; spreading / `.some()` on a
  // non-array THROWS — and a throw here means the function never returns a verdict,
  // silently defeating the non-negotiable operator escape (it locks out admin/owner
  // too). Fail-closed-to-empty preserves the hard floor under garbage config.
  const allowRoles = Array.isArray(cfg.allowRoles) ? cfg.allowRoles : []
  const allow = new Set<string>([...allowRoles, ...OPERATOR_ROLES])
  const roles = Array.isArray(principalRoles) ? principalRoles : []
  if (roles.some((r) => allow.has(r))) return { blocked: false } // admin escape hatch (hard floor)

  const retryAfter = cfg.retryAfterSec ?? DEFAULT_RETRY_AFTER_SEC
  return {
    blocked: true,
    status: 503,
    headers: { 'Retry-After': String(retryAfter) },
    message: cfg.message,
  }
}
