/**
 * # blotter/protocol-view — the pure engine → PROTOCOL Blotter projection (kestrel-b0li).
 *
 * The ONE source of truth for turning a rich engine {@link EngineBlotter} into the generic public
 * PROTOCOL {@link Blotter} the {@link grade} Judge scores — un-dropping the risk-honest `totals` +
 * `fill_claim` (kestrel-h314 D2) so both metric families are outsider-recomputable from a published
 * Blotter. Extracted here from `sdk/local.ts` (which re-exports it) into a LIGHT module — it depends
 * only on the blotter types + the dependency-free protocol types, never on `lang`/`session`/`catalog`
 * — so the node-only `certify` verb can grade a re-projection (`published numbers == grade(reproject(tape))`,
 * OSS-ADR-0051) without dragging the engine drive into the npx-light import graph.
 *
 * Honest + deterministic: no wall clock (the settle instant is the settle-mark's `as_of_ts` or the
 * latest plan-lifecycle ts), no RNG, no invented arithmetic — only the accounting the engine record
 * already carries, reduced to its public verdict.
 */

import type { Blotter as EngineBlotter } from "./index.ts";
import type { Blotter, FillClaim, SettleMarkVerdict, Totals } from "../protocol/index.ts";
import type { SessionId } from "../protocol/session.ts";

/** The settle instant as an ISO-8601 string — from the settle-mark's `as_of_ts` when the graded bus
 * carries one, else the latest plan-lifecycle ts (deterministic; never a wall clock). */
function settledAtOf(b: EngineBlotter): string {
  const asOf = b.totals.settle_mark?.as_of_ts;
  if (typeof asOf === "number") return new Date(asOf).toISOString();
  let maxTs = 0;
  for (const p of b.plans) for (const s of p.lifecycle) if (s.ts > maxTs) maxTs = s.ts;
  return new Date(maxTs).toISOString();
}

/**
 * Project the rich engine {@link EngineBlotter}'s totals into the verdict-only PROTOCOL {@link Totals}
 * (kestrel-h314 D2). UN-DROPS the risk-honest accounting the engine record already carries — byte-stable,
 * NOT invented arithmetic — so the risk-honest metrics are outsider-recomputable from the published Blotter.
 * The engine's licensed raw settle mark is REDUCED to its verdict here (D3): the raw vendor spot +
 * observation timestamps do NOT cross onto the public artifact.
 */
function toProtocolTotals(b: EngineBlotter): Totals {
  const t = b.totals;
  const sm = t.settle_mark;
  const settle_mark: SettleMarkVerdict | undefined =
    sm === undefined ? undefined : { stale: sm.stale, source: sm.source, mark_uncertain: sm.mark_uncertain, note: sm.note };
  return {
    floor: t.floor,
    expected: t.expected,
    ...(t.sampled !== undefined ? { sampled: t.sampled } : {}),
    headline: {
      channel: t.headline.channel,
      usd: t.headline.usd,
      gate: { qualified: t.headline.gate.qualified, reasons: t.headline.gate.reasons },
      tainted: t.headline.tainted,
      reasons: t.headline.reasons,
    },
    ...(settle_mark !== undefined ? { settle_mark } : {}),
  };
}

/** Project the engine `fill_claim` slices onto the wire {@link FillClaim} (kestrel-h314 D2): drops the
 * per-slice `orders` count (not needed to recompute bankable EV), keeps the SIGNED `expected`/`gain`/`loss`.
 * Deterministic, in the fixed `[calibrated, extrapolated]` order the engine already emits. */
function toProtocolFillClaim(b: EngineBlotter): readonly FillClaim[] {
  return b.fill_claim.map((c) => ({ support: c.support, expected: c.expected, gain: c.gain, loss: c.loss }));
}

/**
 * Project the rich engine {@link EngineBlotter} into the PROTOCOL {@link Blotter} (the generic public
 * view). Honest + deterministic. Carries the deterministic identity + the fully-determined settlement;
 * the generic order/fill/position arrays are empty (the engine record lacks the generic legs a faithful
 * public Instrument/Order needs; never fabricated). Additionally carries the un-dropped `totals` +
 * `fill_claim`, so the risk-honest metric family is outsider-recomputable by the {@link grade} Judge.
 */
export function toProtocolBlotter(sessionId: SessionId, b: EngineBlotter): Blotter {
  return {
    sessionId,
    orders: [],
    fills: [],
    positions: [],
    settlement: { realized: b.totals.floor, asset: "usd", settledAt: settledAtOf(b) },
    totals: toProtocolTotals(b),
    fill_claim: toProtocolFillClaim(b),
  };
}
