/**
 * # src/oversight/project.ts — the TOTAL Kernel projector (the conformance point)
 *
 * `projectKernel(k: Kernel): OversightKernel` is the ONE place the runtime's acting
 * {@link ../frame/types.ts Kernel} (14 sections, `src/frame/types.ts`) is mapped onto the protocol's
 * structural mirror {@link ../protocol/oversight.ts OversightKernel}. The protocol imports NOTHING
 * (it is a dependency-free leaf), so drift between the two shapes can be caught in EXACTLY one
 * place — here — and it is caught **AT COMPILE TIME**.
 *
 * ## The key-witness (the compile-time fence)
 * {@link SECTION_PROJECTORS} is typed `{ readonly [K in keyof Kernel]: (k: Kernel) => OversightKernel[K] }`.
 * Its keys are EXACTLY `keyof Kernel`, and each value must return the matching `OversightKernel`
 * field. Adding a section to `Kernel` therefore breaks the build in two independent ways:
 *   1. the {@link SECTION_PROJECTORS} literal is now MISSING a key → red until a projector is written;
 *   2. `OversightKernel[newKey]` does not resolve until the protocol mirror grows the section too.
 * The paired {@link PROJECTED_KERNEL_SECTIONS} carries the bead's named `Record<keyof Kernel, true>`
 * roster. A new Kernel section thus becomes a **compile error, never a silent drop** — the exact
 * failure the golden `OversightFrame` fixture (tests/golden/oversight-frame.golden.json) also reds on
 * once the section reaches the wire. A projector that merely spread `{...k}` would let a new section
 * drop silently (red-on-inert); this one cannot.
 *
 * ## Total, pure, absent-not-hidden
 * PURE — no clock, no I/O, no RNG. TOTAL — an absent source section (the cockpit sections are optional
 * on `Kernel` so the pre-cockpit construction stays valid) projects to an explicit `null`/`[]`, NEVER
 * a dropped key (contract invariant 6). So the wire shape carries no optionality to be silent with.
 *
 * ## Deliberate mirror exclusions (recorded, never silent)
 *  - {@link ../frame/types.ts FillRecord}`.clock` is DROPPED (a wall clock is permitted only on
 *    `SpectatorFrame.asof`; fill order rides the Bus `seq`).
 *  - {@link ../frame/types.ts BudgetEnvelope}`.sizing` (`SizingHeadroom`) is DROPPED (an agent-authoring
 *    aid, not PM oversight state — a PM never authors tickets).
 * Both are documented at the protocol shapes (`KernelFill` / `RiskEnvelope`) and here.
 */

import type {
  Brief,
  Budget,
  BudgetEnvelope,
  Claim,
  EngineAction,
  Field,
  FillRecord,
  Kernel,
  Mandate,
  OwnerAct,
  PlanStateEntry,
  Position,
  RestingOrder,
  VehicleHealth,
  WakeRef,
} from "../frame/types.ts";
import type {
  KernelBrief,
  KernelBudget,
  KernelClaim,
  KernelEngineAction,
  KernelField,
  KernelFill,
  KernelLeg,
  KernelMandate,
  KernelOwnerAct,
  KernelPlanState,
  KernelPosition,
  KernelRestingOrder,
  KernelVehicleHealth,
  KernelWake,
  OversightKernel,
  RiskEnvelope,
} from "../protocol/oversight.ts";

/* ── Per-shape projections ─────────────────────────────────────────────────── */

/** A DATE-BLIND leg (`symbol` + optional `strike`/`right`). Builds without ever assigning `undefined`
 *  to an optional key (exactOptionalPropertyTypes). Shared by position/resting/fill projection. */
function projectLeg(src: {
  readonly instrument: string;
  readonly strike?: number;
  readonly right?: "C" | "P";
}): KernelLeg {
  const leg: { symbol: string; strike?: number; right?: "C" | "P" } = { symbol: src.instrument };
  if (src.strike !== undefined) leg.strike = src.strike;
  if (src.right !== undefined) leg.right = src.right;
  return leg;
}

/** A {@link Field} → {@link KernelField}: same shape, but built key-by-key so an absent optional is
 *  never materialized as `undefined` (exactOptionalPropertyTypes). */
function projectField(f: Field): KernelField {
  const out: {
    value: number;
    attribution: "OBS" | "CALC" | "MODEL";
    source?: string;
    modelVer?: string;
    confidence?: number;
    asOfSeq?: number;
  } = { value: f.value, attribution: f.attribution };
  if (f.source !== undefined) out.source = f.source;
  if (f.modelVer !== undefined) out.modelVer = f.modelVer;
  if (f.confidence !== undefined) out.confidence = f.confidence;
  if (f.asOfSeq !== undefined) out.asOfSeq = f.asOfSeq;
  return out;
}

function projectMandate(m: Mandate | null | undefined): KernelMandate | null {
  if (m === undefined || m === null) return null;
  return {
    objective: m.objective,
    rUsd: m.rUsd,
    successCriterion: m.successCriterion,
    riskRule: m.riskRule,
  };
}

function projectBrief(b: Brief | null | undefined): KernelBrief | null {
  if (b === undefined || b === null) return null;
  return { text: b.text, hash: b.hash, version: b.version ?? null };
}

function projectWake(w: WakeRef | null | undefined): KernelWake | null {
  if (w === undefined || w === null) return null;
  return { reason: w.reason, severity: w.severity, deadlineMin: w.deadline };
}

function projectVehicleHealth(v: VehicleHealth): KernelVehicleHealth {
  return {
    symbol: v.instrument,
    bidPresentRate: v.bidPresentRate,
    twoSided: v.twoSided,
    staleS: v.staleS,
    dark: v.dark,
  };
}

/** DELIBERATE MIRROR EXCLUSION: {@link BudgetEnvelope}`.sizing` (`SizingHeadroom`) is dropped — an
 *  agent-authoring aid, not PM oversight state (a PM never authors tickets). */
function projectBudgetEnvelope(b: BudgetEnvelope | null | undefined): RiskEnvelope | null {
  if (b === undefined || b === null) return null;
  return {
    remainingR: b.remainingR,
    planEnvelope: b.planEnvelope,
    bookEnvelope: b.bookEnvelope,
    ownerEnvelope: b.ownerEnvelope,
  };
}

function projectOwnerAct(a: OwnerAct): KernelOwnerAct {
  return { id: a.id, kind: a.kind, asofSeq: a.asofSeq };
}

function projectEngineAction(a: EngineAction): KernelEngineAction {
  return { id: a.id, kind: a.kind, asofSeq: a.asofSeq, reason: a.reason ?? null };
}

function projectClaim(c: Claim): KernelClaim {
  return { field: projectField(c.field), claimType: c.claimType };
}

function projectPosition(p: Position): KernelPosition {
  return {
    leg: projectLeg(p),
    qty: p.qty,
    basis: p.basis,
    fair: p.fair ?? null,
    unrealUsd: p.unrealUsd ?? null,
    plan: p.plan ?? null,
    claimOwner: p.claimOwner ?? null,
    structure: p.structure ?? null,
  };
}

function projectResting(o: RestingOrder): KernelRestingOrder {
  return {
    ref: o.ref,
    leg: projectLeg(o),
    side: o.side,
    qty: o.qty,
    px: o.px,
    live: o.live ?? false,
    clamped: o.clamped ?? false,
    note: o.note ?? null,
    plan: o.plan ?? null,
  };
}

/** DELIBERATE MIRROR EXCLUSION: {@link FillRecord}`.clock` is dropped — a wall clock is permitted only
 *  on `SpectatorFrame.asof`; fill order rides the Bus `seq`, never a clock token on the deterministic
 *  path. */
function projectFill(f: FillRecord): KernelFill {
  return { leg: projectLeg(f), side: f.side, qty: f.qty, px: f.px, plan: f.plan ?? null };
}

function projectBudget(b: Budget | null | undefined): KernelBudget | null {
  if (b === undefined || b === null) return null;
  return {
    used: b.used,
    remaining: b.remaining,
    total: b.total ?? null,
    maxConcurrentR: b.maxConcurrentR ?? null,
  };
}

function projectPlan(p: PlanStateEntry): KernelPlanState {
  return {
    name: p.name,
    state: p.state,
    outcome: p.outcome ?? null,
    note: p.note ?? null,
    blockedReason: p.blockedReason ?? null,
  };
}

/* ── The key-witness + the projector map (the compile-time fence) ──────────────── */

/**
 * THE KEY-WITNESS. Its type `{ readonly [K in keyof Kernel]-?: (k: Kernel) => OversightKernel[K] }`
 * makes this object BOTH the projector and the fence (the `-?` strips the optionality Kernel's
 * cockpit sections carry, so every section demands a projector — not just the required ones):
 *  - keys are EXACTLY `keyof Kernel` — a section added to `Kernel` makes this literal miss a key and
 *    fails the build until a projector is written for it;
 *  - each value's return type is pinned to `OversightKernel[K]` — so the section also cannot be added
 *    to `Kernel` alone: `OversightKernel[K]` fails to resolve until the protocol mirror grows it too.
 * A new section therefore CANNOT ship unprojected: it is a compile error, never a silent drop.
 */
const SECTION_PROJECTORS: { readonly [K in keyof Kernel]-?: (k: Kernel) => OversightKernel[K] } = {
  mandate: (k) => projectMandate(k.mandate),
  brief: (k) => projectBrief(k.brief),
  wake: (k) => projectWake(k.wake),
  dataHealth: (k) => (k.dataHealth ?? []).map(projectVehicleHealth),
  unavailable: (k) => [...(k.unavailable ?? [])],
  budgetEnvelope: (k) => projectBudgetEnvelope(k.budgetEnvelope),
  ownerActs: (k) => (k.ownerActs ?? []).map(projectOwnerAct),
  engineLog: (k) => (k.engineLog ?? []).map(projectEngineAction),
  claims: (k) => (k.claims ?? []).map(projectClaim),
  positions: (k) => k.positions.map(projectPosition),
  resting: (k) => k.resting.map(projectResting),
  fillsSinceLast: (k) => k.fillsSinceLast.map(projectFill),
  budget: (k) => projectBudget(k.budget),
  plans: (k) => k.plans.map(projectPlan),
};

/**
 * The bead's named `Record<keyof Kernel, true>` view of {@link SECTION_PROJECTORS} — the section
 * roster, enumerated once. `satisfies Record<keyof Kernel, true>` makes a MISSING or EXTRA section a
 * compile error here as well: a redundant second fence, and the human-readable list of exactly which
 * Kernel sections the oversight seam projects.
 */
export const PROJECTED_KERNEL_SECTIONS = {
  mandate: true,
  brief: true,
  wake: true,
  dataHealth: true,
  unavailable: true,
  budgetEnvelope: true,
  ownerActs: true,
  engineLog: true,
  claims: true,
  positions: true,
  resting: true,
  fillsSinceLast: true,
  budget: true,
  plans: true,
} as const satisfies Record<keyof Kernel, true>;

/**
 * Project the runtime {@link Kernel} onto the protocol {@link OversightKernel}. TOTAL and PURE: every
 * section is emitted (absent ⇒ explicit `null`/`[]`, never a dropped key), and there is no clock, no
 * I/O, no RNG. The `satisfies OversightKernel` on the literal is the third fence — the output is
 * compile-checked TOTAL against the protocol shape.
 */
export function projectKernel(k: Kernel): OversightKernel {
  return {
    mandate: SECTION_PROJECTORS.mandate(k),
    brief: SECTION_PROJECTORS.brief(k),
    wake: SECTION_PROJECTORS.wake(k),
    dataHealth: SECTION_PROJECTORS.dataHealth(k),
    unavailable: SECTION_PROJECTORS.unavailable(k),
    budgetEnvelope: SECTION_PROJECTORS.budgetEnvelope(k),
    ownerActs: SECTION_PROJECTORS.ownerActs(k),
    engineLog: SECTION_PROJECTORS.engineLog(k),
    claims: SECTION_PROJECTORS.claims(k),
    positions: SECTION_PROJECTORS.positions(k),
    resting: SECTION_PROJECTORS.resting(k),
    fillsSinceLast: SECTION_PROJECTORS.fillsSinceLast(k),
    budget: SECTION_PROJECTORS.budget(k),
    plans: SECTION_PROJECTORS.plans(k),
  } satisfies OversightKernel;
}
