/**
 * # cli/render/tables — three render branches for the registry rows (Kestrel CLI v1 §5)
 *
 * Pure functions of `(data, ctx)`. The data is IDENTICAL across the three branches; only the skin
 * changes:
 * - **human**: fixed-width glyph tables (`─` separators, right-aligned signed `usd()`, `✓` marks,
 *   `shortId` truncation), ANSI color gated on `ctx.color`, cells truncated to `ctx.width`.
 * - **text**: one record per line, TAB-separated, no glyphs, no color, full precision (grep/LLM).
 * - **json**: one deterministic object `{schema, rows:[…]}`, keys sorted, numbers as JSON numbers.
 *
 * The Unicode/glyph printers live ONLY in the human branch (the machine branches are plain).
 */

import type { OutputCtx } from "../context.ts";
import type { LeaderboardRow, LineageInstance, PlanInstanceRow, RunRow, RunWithPlans } from "../heavy.ts";
import { isPlanDigest, type HostedRunRow } from "../../ledger/hosted.ts";

// ─────────────────────────────────────────────────────────────────────────────
// Formatting primitives (human money glyph + padding) — human branch only
// ─────────────────────────────────────────────────────────────────────────────

const GREEN = "[32m";
const RED = "[31m";
const DIM = "[2m";
const BOLD = "[1m";
const RESET = "[0m";

function pad(s: string, w: number): string {
  return s.length >= w ? s : s + " ".repeat(w - s.length);
}
function padL(s: string, w: number): string {
  return s.length >= w ? s : " ".repeat(w - s.length) + s;
}
/** Signed dollars, 2dp (`+25.00`, `-270.00`) — the human money glyph. */
function usd(n: number): string {
  return (n >= 0 ? "+" : "") + n.toFixed(2);
}
function shortId(id: string): string {
  return id.slice(0, 12);
}
/**
 * A compact, legible plan/strategy LABEL for the runs table (kestrel-etyu): the `--plans` file's
 * BASENAME or the stand-down/bundled default's short head (its trailing "— supply --plans …" hint
 * dropped). An absent field (an old receipt written before the field existed) renders `—` — UNKNOWN,
 * fail-soft on the display path, never a fault. The label is for READABILITY; two runs of one path
 * edited in place share it — the {@link shortDigest} column is what tells them apart. The FULL label
 * is preserved verbatim in `runs show` and every json payload; this only trims the one-line list cell.
 */
function compactStrategy(strategy: string | undefined): string {
  if (strategy === undefined || strategy === "") return "—";
  const head = strategy.split(" — ")[0]!; // drop the demo label's trailing "— supply --plans <file> …"
  return head.slice(head.lastIndexOf("/") + 1); // basename of a --plans path (no-op when there is no `/`)
}
/** The plan CONTENT identity, trimmed to a legible 8-hex prefix for the list cell (kestrel-etyu). This
 * is the field that distinguishes two runs of ONE `--plans` path edited in place. `—` for anything
 * {@link isPlanDigest} calls UNKNOWN (absent, empty, malformed) — the SAME predicate `samePlan` reads,
 * so the list cell and the compare verdict can never disagree about what is known. */
function shortDigest(digest: string | undefined): string {
  return isPlanDigest(digest) ? digest.slice(0, 8) : "—";
}

/**
 * A DISPLAY-only column: header + alignment + a human cell string. Used by the human/text branches
 * ({@link glyphTable}/{@link tsv}), which need nothing else. `json` is optional HERE and serves one
 * purpose — a `money` column's colorizer reads it for the numeric value.
 *
 * This is the weaker of the two column types on purpose. {@link Col} (below) is what a json-rendered
 * table must use, and it REQUIRES the json pair — so `tsc`, not a docstring, keeps every column of a
 * machine schema in that schema.
 */
interface DisplayCol<R> {
  readonly header: string;
  readonly right: boolean;
  readonly cell: (r: R) => string; // shared by human + text
  /** When true, positive→green / negative→red in the human branch (money columns). Needs {@link json}. */
  readonly money?: boolean;
  /** The numeric/raw value — read ONLY by the money colorizer on a display column. */
  readonly json?: (r: R) => unknown;
}

/**
 * A JSON-PROJECTED column: a {@link DisplayCol} that additionally names its wire (key,value) pair.
 * `jsonKey`/`json` are REQUIRED — a table rendered through {@link jsonRowObjs} takes `Col<R>[]`, so a
 * column added without a json projection is a COMPILE error, never a field that silently vanishes from
 * `kestrel.runs/v1` / `kestrel.lineage/v1` / `kestrel.leaderboard/v1`.
 */
interface Col<R> extends DisplayCol<R> {
  readonly jsonKey: string;
  readonly json: (r: R) => unknown;
}

/** Render a header + rows as a fixed-width glyph table with optional color (human branch). */
function glyphTable<R>(cols: readonly DisplayCol<R>[], rows: readonly R[], ctx: OutputCtx): string {
  const headers = cols.map((c) => c.header);
  const body = rows.map((r) => cols.map((c) => c.cell(r)));
  const widths = headers.map((h, c) => Math.max(h.length, ...body.map((row) => (row[c] ?? "").length)));
  const colorCell = (raw: string, c: number, r: R): string => {
    if (!ctx.color) return raw;
    const col = cols[c]!;
    if (col.money && col.json !== undefined) {
      const v = col.json(r);
      if (typeof v === "number") return v >= 0 ? `${GREEN}${raw}${RESET}` : `${RED}${raw}${RESET}`;
    }
    return raw;
  };
  const fmtHeader = headers.map((h, c) => (cols[c]!.right ? padL(h, widths[c]!) : pad(h, widths[c]!)));
  const headerLine = ctx.color ? fmtHeader.map((h) => `${BOLD}${h}${RESET}`).join("  ") : fmtHeader.join("  ");
  const sepRaw = widths.map((w) => "─".repeat(w)).join("  ");
  const sep = ctx.color ? `${DIM}${sepRaw}${RESET}` : sepRaw;
  const bodyLines = rows.map((r, ri) =>
    cols
      .map((col, c) => {
        const raw = body[ri]![c]!;
        const padded = col.right ? padL(raw, widths[c]!) : pad(raw, widths[c]!);
        return colorCell(padded, c, r);
      })
      .join("  "),
  );
  return [headerLine, sep, ...bodyLines].join("\n");
}

/** Text branch: TSV of the shared cell() strings, no header, no color. */
function tsv<R>(cols: readonly DisplayCol<R>[], rows: readonly R[]): string {
  return rows.map((r) => cols.map((c) => c.cell(r)).join("\t")).join("\n");
}

/** The per-row `{key:value}` object for the json branch (keys sorted for determinism). */
function jsonRowObjs<R>(cols: readonly Col<R>[], rows: readonly R[]): Record<string, unknown>[] {
  return rows.map((r) => {
    const o: Record<string, unknown> = {};
    for (const c of cols) o[c.jsonKey] = c.json(r);
    return sortKeys(o);
  });
}

/** Json branch: `{schema, rows:[{key:value}]}`, keys sorted for determinism. */
function jsonRows<R>(schema: string, cols: readonly Col<R>[], rows: readonly R[]): string {
  return JSON.stringify({ schema, rows: jsonRowObjs(cols, rows) });
}

/** Deterministic key ordering for machine JSON. */
function sortKeys<T extends Record<string, unknown>>(o: T): Record<string, unknown> {
  const out: Record<string, unknown> = {};
  for (const k of Object.keys(o).sort()) out[k] = o[k];
  return out;
}

// ─────────────────────────────────────────────────────────────────────────────
// runs list
// ─────────────────────────────────────────────────────────────────────────────

const RUN_COLS: readonly Col<RunRow>[] = [
  // `source` labels the run's lineage (`local` = a `run`/`day` session recorded here; `hosted` = a
  // `sim` receipt, rendered from HOSTED_COLS below) so a merged `runs list` is legible (kestrel-pvlc).
  { header: "source", right: false, cell: () => "local", jsonKey: "source", json: () => "local" },
  { header: "run_id", right: false, cell: (r) => shortId(r.run_id), jsonKey: "run_id", json: (r) => r.run_id },
  { header: "date", right: false, cell: (r) => r.session_date, jsonKey: "session_date", json: (r) => r.session_date },
  { header: "mode", right: false, cell: (r) => r.mode, jsonKey: "mode", json: (r) => r.mode },
  { header: "fill_model", right: false, cell: (r) => r.fill_model, jsonKey: "fill_model", json: (r) => r.fill_model },
  { header: "cal", right: false, cell: (r) => (r.calibrated ? "✓" : ""), jsonKey: "calibrated", json: (r) => Boolean(r.calibrated) },
  { header: "realized", right: true, money: true, cell: (r) => usd(r.realized_floor_usd), jsonKey: "realized_floor_usd", json: (r) => r.realized_floor_usd },
  { header: "expected", right: true, money: true, cell: (r) => usd(r.expected_usd), jsonKey: "expected_usd", json: (r) => r.expected_usd },
  { header: "premium", right: true, cell: (r) => r.premium_spent.toFixed(2), jsonKey: "premium_spent", json: (r) => r.premium_spent },
  { header: "events", right: true, cell: (r) => String(r.bus_events), jsonKey: "bus_events", json: (r) => r.bus_events },
];

/** The hosted-lineage columns (`sim` receipts, kestrel-pvlc). Same three skins as the local runs;
 * the shared `source` column ties the two lineages into one `runs list`. The human/text branches show
 * a readable subset; the json branch emits the FULL {@link HostedRunRow} (every artifact id + the
 * signed timestamp) so an agent can re-resolve the whole receipt, not just what fits a glyph table.
 *
 * Hence {@link DisplayCol}, not {@link Col}: these columns are the human/text SUBSET and never project
 * the wire schema (the spread in {@link renderRuns} does), so they carry no `jsonKey`/`json` pair — and
 * the type makes passing them to {@link jsonRowObjs} a compile error rather than a silent half-schema. */
const HOSTED_COLS: readonly DisplayCol<HostedRunRow>[] = [
  { header: "source", right: false, cell: (h) => h.source },
  { header: "scenario", right: false, cell: (h) => h.slug },
  // The plan that produced the run (kestrel-etyu) — the human LABEL for readability + the CONTENT
  // digest that actually distinguishes two runs of ONE path edited in place ("did my tweak help?").
  { header: "strategy", right: false, cell: (h) => compactStrategy(h.strategy) },
  { header: "digest", right: false, cell: (h) => shortDigest(h.strategy_digest) },
  { header: "operation", right: false, cell: (h) => h.operation_id },
  { header: "orders", right: true, cell: (h) => String(h.order_count) },
  { header: "fills", right: true, cell: (h) => String(h.fill_count) },
  // `json` here is NOT a wire projection — it is the money colorizer's numeric accessor (see DisplayCol).
  { header: "realized", right: true, money: true, cell: (h) => usd(h.realized_pnl), json: (h) => h.realized_pnl },
  { header: "proof", right: false, cell: (h) => h.proof_url },
];

/**
 * Render the local runs and the hosted-lineage receipts as ONE `runs list` (kestrel-pvlc). The two
 * lineages carry different columns, so human/text keep them as two labeled sections; json merges them
 * into a single self-describing `rows` array (each row's `source` names its lineage). An all-empty
 * result stays the prior `(no runs)` / empty-payload sentinel byte-for-byte.
 */
export function renderRuns(rows: readonly RunRow[], ctx: OutputCtx, hosted: readonly HostedRunRow[] = []): string {
  if (ctx.mode === "json") {
    const local = jsonRowObjs(RUN_COLS, rows);
    // The full receipt, not the glyph subset — sorted keys for determinism, like every json row.
    const hostedObjs = hosted.map((h) => sortKeys({ ...h }));
    return JSON.stringify({ schema: "kestrel.runs/v1", rows: [...local, ...hostedObjs] }) + "\n";
  }
  if (ctx.mode === "text") {
    const parts: string[] = [];
    if (rows.length > 0) parts.push(tsv(RUN_COLS, rows));
    if (hosted.length > 0) parts.push(tsv(HOSTED_COLS, hosted));
    return parts.length === 0 ? "" : parts.join("\n") + "\n";
  }
  if (rows.length === 0 && hosted.length === 0) return "(no runs)\n";
  const parts: string[] = [];
  if (rows.length > 0) parts.push(glyphTable(RUN_COLS, rows, ctx) + `\n\n${rows.length} run(s)`);
  if (hosted.length > 0) parts.push(glyphTable(HOSTED_COLS, hosted, ctx) + `\n\n${hosted.length} hosted run(s)`);
  return parts.join("\n\n") + "\n";
}

// ─────────────────────────────────────────────────────────────────────────────
// runs show (a run header block + its plans table)
// ─────────────────────────────────────────────────────────────────────────────

const PLAN_COLS: readonly Col<PlanInstanceRow>[] = [
  { header: "plan", right: false, cell: (p) => p.name, jsonKey: "name", json: (p) => p.name },
  { header: "final_state", right: false, cell: (p) => p.final_state, jsonKey: "final_state", json: (p) => p.final_state },
  { header: "outcome", right: false, cell: (p) => p.outcome ?? "—", jsonKey: "outcome", json: (p) => p.outcome },
  { header: "fired", right: true, cell: (p) => (p.fired ? "yes" : "no"), jsonKey: "fired", json: (p) => Boolean(p.fired) },
  { header: "orders", right: true, cell: (p) => String(p.orders), jsonKey: "orders", json: (p) => p.orders },
  { header: "fills", right: true, cell: (p) => String(p.fills), jsonKey: "fills", json: (p) => p.fills },
  { header: "realized", right: true, money: true, cell: (p) => usd(p.realized_usd), jsonKey: "realized_usd", json: (p) => p.realized_usd },
  { header: "expected", right: true, money: true, cell: (p) => usd(p.expected_usd), jsonKey: "expected_usd", json: (p) => p.expected_usd },
];

export function renderRunShow(rec: RunWithPlans, ctx: OutputCtx): string {
  const r = rec.run;
  if (ctx.mode === "json") {
    const run: Record<string, unknown> = {
      run_id: r.run_id,
      session_date: r.session_date,
      mode: r.mode,
      instruments: r.instruments,
      fill_model: r.fill_model,
      calibrated: Boolean(r.calibrated),
      recorded_at: r.recorded_at,
      r_usd: r.r_usd,
      realized_floor_usd: r.realized_floor_usd,
      expected_usd: r.expected_usd,
      premium_spent: r.premium_spent,
      bus_events: r.bus_events,
      bus_sha256: r.bus_sha256,
      plans_sha256: r.plans_sha256,
      determinism_hash: r.determinism_hash,
      report_path: r.report_path,
    };
    const plans = rec.plans.map((p) =>
      sortKeys({
        name: p.name,
        final_state: p.final_state,
        outcome: p.outcome,
        fired: Boolean(p.fired),
        orders: p.orders,
        fills: p.fills,
        realized_usd: p.realized_usd,
        expected_usd: p.expected_usd,
      }),
    );
    return JSON.stringify({ schema: "kestrel.run/v1", run: sortKeys(run), plans }) + "\n";
  }

  if (ctx.mode === "text") {
    const lines = [
      `run_id\t${r.run_id}`,
      `session_date\t${r.session_date}`,
      `mode\t${r.mode}`,
      `instruments\t${r.instruments}`,
      `fill_model\t${r.fill_model}`,
      `calibrated\t${Boolean(r.calibrated)}`,
      `recorded_at\t${r.recorded_at}`,
      `r_usd\t${r.r_usd}`,
      `realized_floor_usd\t${r.realized_floor_usd}`,
      `expected_usd\t${r.expected_usd}`,
      `premium_spent\t${r.premium_spent}`,
      `bus_events\t${r.bus_events}`,
      `bus_sha256\t${r.bus_sha256}`,
      `determinism_hash\t${r.determinism_hash}`,
      `report_path\t${r.report_path ?? ""}`,
    ];
    const plansTsv = rec.plans.length === 0 ? "" : "\n" + tsv(PLAN_COLS, rec.plans);
    return lines.join("\n") + plansTsv + "\n";
  }

  // human
  const head = [
    `run_id       ${r.run_id}`,
    `session      ${r.session_date}  ${r.mode}  [${r.instruments}]`,
    `fill_model   ${r.fill_model}${r.calibrated ? "  (calibrated)" : ""}`,
    `recorded_at  ${r.recorded_at}  (settle ts, injected)`,
    `r_usd        ${r.r_usd.toFixed(2)}`,
    `totals       realized ${usd(r.realized_floor_usd)}  expected ${usd(r.expected_usd)}  premium ${r.premium_spent.toFixed(2)}`,
    `bus          ${r.bus_events} events  sha ${shortId(r.bus_sha256)}  det ${shortId(r.determinism_hash)}`,
    r.report_path !== null ? `report_path  ${r.report_path}` : "report_path  (none)",
    "",
  ].join("\n");
  const plansTable = rec.plans.length === 0 ? "(no plan instances)\n" : glyphTable(PLAN_COLS, rec.plans, ctx) + "\n";
  return head + "\n" + plansTable;
}

/**
 * Re-print a HOSTED `sim` receipt (kestrel-pvlc) — the full print-once story the sim showed exactly once
 * on stdout, resolved back from the local `hosted-runs.jsonl` lineage: the scenario, the grade digest,
 * every durable evidence artifact id, and the shareable proof URL (the last human line, mirroring the sim
 * story). A distinct `kestrel.hosted-run/v1` json schema so a hosted receipt is never mistaken for a local
 * run's shape. Pure text — this render path is node-light, like the store it reads.
 */
export function renderHostedRunShow(h: HostedRunRow, ctx: OutputCtx): string {
  if (ctx.mode === "json") {
    return JSON.stringify({ schema: "kestrel.hosted-run/v1", hosted_run: sortKeys({ ...h }) }) + "\n";
  }
  if (ctx.mode === "text") {
    return (
      [
        `source\t${h.source}`,
        `operation_id\t${h.operation_id}`,
        `slug\t${h.slug}`,
        `requested_slug\t${h.requested_slug}`,
        `strategy\t${h.strategy ?? ""}`,
        `strategy_digest\t${isPlanDigest(h.strategy_digest) ? h.strategy_digest : ""}`,
        `order_count\t${h.order_count}`,
        `fill_count\t${h.fill_count}`,
        `realized_pnl\t${h.realized_pnl}`,
        // The grader's never-fired reason (kestrel-kglw) — present ONLY on a re-shown 0-order run that
        // carried one, so a returning user re-reads WHY nothing fired instead of a bare order_count=0.
        ...(h.never_fired_reason !== undefined ? [`never_fired_reason\t${h.never_fired_reason}`] : []),
        `grade_artifact_id\t${h.grade_artifact_id}`,
        `bus_artifact_id\t${h.bus_artifact_id}`,
        `manifest_artifact_id\t${h.manifest_artifact_id}`,
        `issued_at\t${h.issued_at}`,
        ...(h.copy_token !== undefined ? [`continues_copy_token\t${h.copy_token}`] : []),
        `proof_url\t${h.proof_url}`,
      ].join("\n") + "\n"
    );
  }
  // human — the proof URL is the LAST line, mirroring the sim story's ordering.
  const aliasLine = h.requested_slug !== h.slug ? [`alias        ${h.requested_slug} → ${h.slug}`] : [];
  // The recorded continuation lineage (kestrel-gu9h): the opaque copy-token this run continued from.
  const lineageLine = h.copy_token !== undefined ? [`continues    copy-token ${h.copy_token}`] : [];
  return (
    [
      `operation    ${h.operation_id}  (source: hosted)`,
      `scenario     ${h.slug}`,
      ...aliasLine,
      `strategy     ${h.strategy ?? "(unknown — receipt predates the plan field)"}`,
      `plan digest  ${isPlanDigest(h.strategy_digest) ? h.strategy_digest : "(unknown — predates the plan digest, or unusable)"}`,
      `grade        order_count=${h.order_count}  fill_count=${h.fill_count}  realized_pnl=${usd(h.realized_pnl)}`,
      // The never-fired reason (kestrel-kglw): a re-shown 0-order run states WHY nothing fired, exactly
      // as the sim story did once — so `runs show` is never the bare `order_count=0` the receipt used to
      // re-print (the silent-gate class, one surface downstream). Only when the receipt carried a reason.
      ...(h.never_fired_reason !== undefined ? [`why          no orders fired — ${h.never_fired_reason}`] : []),
      `evidence     manifest=${h.manifest_artifact_id} bus=${h.bus_artifact_id} grade=${h.grade_artifact_id}`,
      ...lineageLine,
      `issued_at    ${h.issued_at === "" ? "(unstamped)" : h.issued_at}`,
      `proof        ${h.proof_url}`,
      "",
    ].join("\n")
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// runs compare (kestrel-etyu) — diff two hosted `sim` receipts, "did my tweak help?"
// ─────────────────────────────────────────────────────────────────────────────

/** Signed integer delta glyph (`+3`, `-1`, `0`) — deterministic, no locale. */
function signedInt(n: number): string {
  return (n > 0 ? "+" : "") + String(n);
}
/** Signed 2dp money delta (`+12.00`, `-12.00`) reusing {@link usd}. */
function signedUsd(n: number): string {
  return usd(n);
}

/**
 * The plan-identity relationship between two receipts (kestrel-etyu): do they carry the SAME plan
 * CONTENT? `true`/`false` only when BOTH digests are KNOWN per {@link isPlanDigest}; `null` whenever
 * either is unknown (absent, empty, or malformed) — never a guessed verdict in EITHER direction. This
 * is the signal that makes "did my tweak help?" answerable even when both runs used the SAME `--plans`
 * path (an in-place edit moves the digest).
 *
 * It reads the shared predicate rather than an `=== undefined` check on purpose: a bare undefined-check
 * let an empty-string digest count as known, so two UNKNOWN digests compared EQUAL and this returned
 * `true` — "A and B ran the SAME plan content" — for runs the list was rendering `—`. Unknown ≠ match.
 */
function samePlan(a: HostedRunRow, b: HostedRunRow): boolean | null {
  if (!isPlanDigest(a.strategy_digest) || !isPlanDigest(b.strategy_digest)) return null;
  return a.strategy_digest === b.strategy_digest;
}

/**
 * Diff two HOSTED `sim` receipts (kestrel-etyu) — the returning user's "did my tweak help?" job. `a`
 * is the baseline, `b` the tweak; every delta is `b − a` (how B moved from A). It shows WHICH plan
 * each run used — its LABEL *and* its CONTENT digest, plus whether the two are the same plan content
 * — and diffs the graded activity metrics (orders, fills, realized P&L). The digest is what makes the
 * in-place-edit workflow work: two runs of ONE `--plans` path share a label but differ by digest.
 *
 * Pure + deterministic: integer/`toFixed` arithmetic + a hex compare over the two rows only — no wall
 * clock, no RNG, so the same pair renders byte-identically. A missing `strategy`/`strategy_digest` (a
 * pre-field receipt) reads `(unknown)` and yields `same_plan: null`; comparing two DIFFERENT scenarios
 * is not blocked but is flagged, since cross-scenario P&L is not directly comparable.
 */
export function renderRunsCompare(a: HostedRunRow, b: HostedRunRow, ctx: OutputCtx): string {
  const dOrders = b.order_count - a.order_count;
  const dFills = b.fill_count - a.fill_count;
  const dPnl = b.realized_pnl - a.realized_pnl;
  const stratA = a.strategy ?? "(unknown)";
  const stratB = b.strategy ?? "(unknown)";
  const sameScenario = a.slug === b.slug;
  const same = samePlan(a, b);

  if (ctx.mode === "json") {
    return (
      JSON.stringify({
        schema: "kestrel.runs-compare/v1",
        same_scenario: sameScenario,
        // Whether A and B ran the SAME plan CONTENT (by digest); null when either digest is unknown.
        same_plan: same,
        a: sortKeys({ ...a }),
        b: sortKeys({ ...b }),
        // Deltas are B − A (the tweak relative to the baseline) — the activity family the receipt carries.
        delta: sortKeys({ order_count: dOrders, fill_count: dFills, realized_pnl: dPnl }),
      }) + "\n"
    );
  }

  if (ctx.mode === "text") {
    const lines = [
      `a_operation_id\t${a.operation_id}`,
      `b_operation_id\t${b.operation_id}`,
      `a_scenario\t${a.slug}`,
      `b_scenario\t${b.slug}`,
      `same_scenario\t${sameScenario}`,
      `a_strategy\t${a.strategy ?? ""}`,
      `b_strategy\t${b.strategy ?? ""}`,
      `a_strategy_digest\t${isPlanDigest(a.strategy_digest) ? a.strategy_digest : ""}`,
      `b_strategy_digest\t${isPlanDigest(b.strategy_digest) ? b.strategy_digest : ""}`,
      `same_plan\t${same === null ? "unknown" : same}`,
      `metric\ta\tb\tdelta`,
      `order_count\t${a.order_count}\t${b.order_count}\t${dOrders}`,
      `fill_count\t${a.fill_count}\t${b.fill_count}\t${dFills}`,
      `realized_pnl\t${a.realized_pnl}\t${b.realized_pnl}\t${dPnl}`,
    ];
    return lines.join("\n") + "\n";
  }

  // human — a small metric table (A | B | Δ) under a header naming each run's scenario + plan (label
  // AND content digest). The plan line states whether A and B ran the same plan content.
  const metrics: readonly { label: string; a: string; b: string; d: string }[] = [
    { label: "orders", a: String(a.order_count), b: String(b.order_count), d: signedInt(dOrders) },
    { label: "fills", a: String(a.fill_count), b: String(b.fill_count), d: signedInt(dFills) },
    { label: "realized", a: usd(a.realized_pnl), b: usd(b.realized_pnl), d: signedUsd(dPnl) },
  ];
  const wLabel = Math.max(6, ...metrics.map((m) => m.label.length));
  const wA = Math.max(1, "A".length, ...metrics.map((m) => m.a.length));
  const wB = Math.max(1, "B".length, ...metrics.map((m) => m.b.length));
  const wD = Math.max(1, "Δ".length, ...metrics.map((m) => m.d.length));
  const planNote =
    same === null
      ? "plan       content identity UNKNOWN (a receipt predates the plan digest, or carries an unusable one)"
      : same
        ? "plan       A and B ran the SAME plan content (digests match)"
        : "plan       A and B ran DIFFERENT plan content (digests differ) — a real tweak";
  const head = [
    `compare · ${sameScenario ? a.slug : `${a.slug} ↔ ${b.slug}`}`,
    `A  ${shortId(a.operation_id)}  ${a.slug}`,
    `     plan  ${stratA}  #${shortDigest(a.strategy_digest)}`,
    `B  ${shortId(b.operation_id)}  ${b.slug}`,
    `     plan  ${stratB}  #${shortDigest(b.strategy_digest)}`,
    planNote,
    ...(sameScenario ? [] : ["note       different scenarios — P&L is not directly comparable"]),
    "",
    `${pad("metric", wLabel)}  ${padL("A", wA)}  ${padL("B", wB)}  ${padL("Δ", wD)}`,
    `${"─".repeat(wLabel)}  ${"─".repeat(wA)}  ${"─".repeat(wB)}  ${"─".repeat(wD)}`,
  ];
  const rows = metrics.map((m) => `${pad(m.label, wLabel)}  ${padL(m.a, wA)}  ${padL(m.b, wB)}  ${padL(m.d, wD)}`);
  return [...head, ...rows, ""].join("\n");
}

// ─────────────────────────────────────────────────────────────────────────────
// lineage
// ─────────────────────────────────────────────────────────────────────────────

const LINEAGE_COLS: readonly Col<LineageInstance>[] = [
  { header: "date", right: false, cell: (i) => i.session_date, jsonKey: "session_date", json: (i) => i.session_date },
  { header: "mode", right: false, cell: (i) => i.mode, jsonKey: "mode", json: (i) => i.mode },
  { header: "fill_model", right: false, cell: (i) => i.fill_model, jsonKey: "fill_model", json: (i) => i.fill_model },
  { header: "final_state", right: false, cell: (i) => i.final_state, jsonKey: "final_state", json: (i) => i.final_state },
  { header: "outcome", right: false, cell: (i) => i.outcome ?? "—", jsonKey: "outcome", json: (i) => i.outcome },
  { header: "fired", right: true, cell: (i) => (i.fired ? "yes" : "no"), jsonKey: "fired", json: (i) => Boolean(i.fired) },
  { header: "fills", right: true, cell: (i) => String(i.fills), jsonKey: "fills", json: (i) => i.fills },
  { header: "realized", right: true, money: true, cell: (i) => usd(i.realized_usd), jsonKey: "realized_usd", json: (i) => i.realized_usd },
  { header: "expected", right: true, money: true, cell: (i) => usd(i.expected_usd), jsonKey: "expected_usd", json: (i) => i.expected_usd },
];

/**
 * Render a plan lineage. `coverage`, when supplied (the command passes it on an EMPTY result,
 * kestrel-qozv), names what lineage DOES track so an empty result is never SILENT: json carries it
 * as a structured `coverage` field (machine-legible, not advisory noise), human appends it under the
 * `(no instances)` sentinel. text stays a pure TSV payload — the command mirrors the notice to stderr.
 */
export function renderLineage(
  name: string,
  rows: readonly LineageInstance[],
  ctx: OutputCtx,
  coverage?: string,
): string {
  if (ctx.mode === "json") {
    const body = JSON.parse(jsonRows("kestrel.lineage/v1", LINEAGE_COLS, rows)) as { schema: string; rows: unknown[] };
    return (
      JSON.stringify({
        schema: body.schema,
        name,
        rows: body.rows,
        ...(coverage !== undefined ? { coverage } : {}),
      }) + "\n"
    );
  }
  if (ctx.mode === "text") return rows.length === 0 ? "" : tsv(LINEAGE_COLS, rows) + "\n";
  if (rows.length === 0)
    return `lineage: ${name}\n\n(no instances)\n${coverage !== undefined ? `\n${coverage}\n` : ""}`;
  const realizedSum = rows.reduce((s, i) => s + i.realized_usd, 0);
  const expectedSum = rows.reduce((s, i) => s + i.expected_usd, 0);
  return (
    `lineage: ${name}\n\n` +
    glyphTable(LINEAGE_COLS, rows, ctx) +
    `\n\n${rows.length} instance(s)  realized ${usd(realizedSum)}  expected ${usd(expectedSum)}\n`
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// leaderboard
// ─────────────────────────────────────────────────────────────────────────────

const LEADER_COLS: readonly Col<LeaderboardRow>[] = [
  { header: "lineage", right: false, cell: (r) => r.name, jsonKey: "name", json: (r) => r.name },
  { header: "runs", right: true, cell: (r) => String(r.runs), jsonKey: "runs", json: (r) => r.runs },
  { header: "fired", right: true, cell: (r) => String(r.fired), jsonKey: "fired", json: (r) => r.fired },
  { header: "fills", right: true, cell: (r) => String(r.fills), jsonKey: "fills", json: (r) => r.fills },
  { header: "realized_sum", right: true, money: true, cell: (r) => usd(r.realized_sum), jsonKey: "realized_sum", json: (r) => r.realized_sum },
  { header: "expected_sum", right: true, money: true, cell: (r) => usd(r.expected_sum), jsonKey: "expected_sum", json: (r) => r.expected_sum },
  { header: "realized_avg", right: true, money: true, cell: (r) => usd(r.realized_avg), jsonKey: "realized_avg", json: (r) => r.realized_avg },
  { header: "expected_avg", right: true, money: true, cell: (r) => usd(r.expected_avg), jsonKey: "expected_avg", json: (r) => r.expected_avg },
];

export function renderLeaderboard(rows: readonly LeaderboardRow[], ctx: OutputCtx): string {
  if (ctx.mode === "json") return jsonRows("kestrel.leaderboard/v1", LEADER_COLS, rows) + "\n";
  if (ctx.mode === "text") return rows.length === 0 ? "" : tsv(LEADER_COLS, rows) + "\n";
  if (rows.length === 0) return "(no lineages)\n";
  return glyphTable(LEADER_COLS, rows, ctx) + `\n\n${rows.length} lineage(s)\n`;
}
