/**
 * # cli/commands/replay — `replay <proofId>` (LIGHT, node-runnable): the reproduction verb (n04e.1).
 *
 * `replay <proofId>` looks the id up in the identity-bound hosted-run ledger (`~/.kestrel/hosted-runs.jsonl`,
 * the same store `runs list` reads, merged with the legacy CWD-local fallback — kestrel-fq78):
 *
 *   - FOUND (a run THIS machine produced) → the row carries the canonical scenario slug, so
 *     replay RE-RUNS that scenario on a fresh anonymous trial and asserts the new grade is
 *     structurally reproducible (same metrics shape) AND its signature re-verifies → a
 *     `REPRODUCED` verdict + the new proof URL.
 *   - NOT FOUND (someone else's proof) → degrades HONESTLY to `verify` semantics with a one-line
 *     notice. This respects no-leak: the public proof never carries the strategy source, so a
 *     stranger's run can never be re-executed — but the verb stays meaningful (it verifies the
 *     published proof instead).
 *
 * The ledger never stores the strategy SOURCE (no-leak holds locally too), so a reproduction
 * re-runs against the labeled stand-down baseline — deterministic for the default front-door
 * run, and honest for a `--plans` run (which cannot be re-executed from a receipt alone).
 *
 * LIGHT: reuses `sim.ts`'s hosted core + the local JSONL ledger (node fs only) + `verify.ts`'s
 * zero-trust check. No bun/chdb, no platform business logic — a thin client of the public API.
 */

import {
  HostedFunnel,
  resolveScenarioSlug,
  type ResolvedScenario,
} from "../backend/hosted.ts";
import {
  readHostedRuns,
  readHostedRunsMerged,
  defaultHostedStorePath,
  LEGACY_CWD_HOSTED_STORE,
  type HostedRunRow,
} from "../../ledger/hosted.ts";
import type { OutputCtx, GlobalFlags } from "../context.ts";
import { parseArgs } from "../args.ts";
import { CliError, EXIT } from "../errors.ts";
import { resolveSimBase, resolveCopyToken, loadStrategy, runHostedProof, type HostedProofOutcome } from "./sim.ts";
import { runVerify, verifyProofSignature, type VerifyVerdict } from "./verify.ts";

type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;

interface Env {
  readonly [k: string]: string | undefined;
}

/** Find the local ledger row a proof id names: by grade artifact id, proof URL, or operation id. */
export function findRun(rows: readonly HostedRunRow[], id: string): HostedRunRow | undefined {
  return rows.find(
    (r) => r.grade_artifact_id === id || r.operation_id === id || r.proof_url.endsWith(`/proof/${id}`),
  );
}

export interface ReplayDeps {
  readonly fetch?: FetchLike;
  readonly env?: Env;
  readonly hostedStorePath?: string;
}

/**
 * `replay <proofId>` — reproduce a local run, or degrade to verifying a published proof. The
 * verdict is DATA on stdout; the exit code reflects it (0 only when the fresh run reproduced
 * AND its signature re-verifies, or when the degraded verify says VERIFIED).
 */
export async function replayCommand(
  argv: readonly string[],
  ctx: OutputCtx,
  globals: GlobalFlags,
  deps: ReplayDeps = {},
): Promise<number> {
  const { positionals, flags } = parseArgs(argv, new Set(), new Set(["copy-token"]));
  if (positionals.length === 0)
    throw new CliError({
      code: "USAGE",
      exit: EXIT.USAGE,
      message: "replay needs a proof id",
      hint: "usage: kestrel replay <proofId>   (a proof this machine produced re-runs; any other is verified)",
    });
  const id = positionals[0]!;
  const env = deps.env ?? (process.env as Env);
  // The identity-bound home store is both the WRITE target (a reproduction's fresh receipt) and the
  // primary READ source (kestrel-fq78). A caller may pin an explicit store (tests) — honored as-is.
  const storePath = deps.hostedStorePath ?? defaultHostedStorePath(env);
  const copyToken = resolveCopyToken(flags, env);
  const forward = copyToken !== undefined ? { copyToken } : {};

  const base = resolveSimBase(globals, env);
  const client = new HostedFunnel({ baseUrl: base, ...(deps.fetch !== undefined ? { fetch: deps.fetch } : {}) });

  // Resolve the proof id across the home store MERGED with the legacy CWD-local fallback, so replay
  // finds a run recorded from ANY directory (kestrel-fq78). An explicitly-pinned store (tests) is the
  // sole source — no implicit relative-path fallback leaks in.
  const rows =
    deps.hostedStorePath !== undefined
      ? readHostedRuns(storePath)
      : readHostedRunsMerged({ home: storePath, fallbacks: [LEGACY_CWD_HOSTED_STORE] });
  const row = findRun(rows, id);

  // ── NOT in the local ledger → honest degrade to `verify` (no-leak: no strategy to replay) ──
  if (row === undefined) {
    return runVerify(
      client,
      id,
      ctx,
      forward,
      "no local strategy to replay — verifying the published proof instead",
    );
  }

  // ── FOUND → re-run the recorded scenario on a fresh anonymous trial ──
  const { entries } = await client.catalog();
  const resolved: ResolvedScenario | null = resolveScenarioSlug(row.slug, entries);
  if (resolved === null) {
    // The recorded scenario is gone from the catalog — reproduce is impossible; verify instead.
    return runVerify(
      client,
      row.grade_artifact_id,
      ctx,
      forward,
      `the recorded scenario ${JSON.stringify(row.slug)} is no longer in the catalog — verifying the recorded proof instead`,
    );
  }

  // The ledger holds no strategy source (no-leak), so reproduce against the stand-down baseline.
  const strategy = loadStrategy(undefined);
  let outcome: HostedProofOutcome | undefined;
  const code = await runHostedProof({
    client,
    resolved,
    strategy,
    ctx,
    hostedStorePath: storePath,
    quiet: true, // replay owns the report
    onProof: (o) => {
      outcome = o;
    },
    ...forward,
  });
  // A gated/settlement re-run already surfaced its Offer (exit 5) — pass it through.
  if (code !== 0) return code;
  if (outcome === undefined)
    // The re-run completed but its fresh proof is still SETTLING (transient R2 write lag,
    // kestrel-fzum) — there is no envelope to re-verify yet, so the reproducibility check cannot
    // conclude. Refuse loudly rather than fabricate a verdict; the re-run itself was not lost.
    throw new CliError({
      code: "GENERIC",
      exit: EXIT.GENERIC,
      message: "replay re-ran the scenario, but the fresh grade proof is still settling (transient R2 write lag) — its signature cannot be re-verified yet",
      hint: "retry `kestrel replay` shortly; the settling warning on stderr names the re-run's operation id and grade artifact id (the proof URL is not yet readable — that is what settling means)",
    });

  // Re-verify the FRESH proof's signature independently (zero-trust), and assert the metrics
  // shape reproduced (the three headline metrics are present + finite).
  const keys = await client.verifyKeys(forward);
  const sig: VerifyVerdict = verifyProofSignature(
    { proof_id: outcome.proofId, root: outcome.root, signature: outcome.signature, kid: outcome.kid, epoch: outcome.epoch, verificationClaim: true },
    keys,
  );
  const metricsReproduced =
    Number.isFinite(outcome.metrics.orderCount) &&
    Number.isFinite(outcome.metrics.fillCount) &&
    Number.isFinite(outcome.metrics.realizedPnl);
  const reproduced = sig === "VERIFIED" && metricsReproduced;
  const verdict = reproduced ? "REPRODUCED" : "NOT_REPRODUCED";

  if (ctx.mode === "json") {
    process.stdout.write(
      JSON.stringify({
        schema: "kestrel.replay/v1",
        verdict,
        scenario: row.slug,
        original_proof: row.proof_url,
        new_proof: outcome.proofUrl,
        signature: sig,
        metrics: outcome.metrics,
      }) + "\n",
    );
  } else {
    process.stdout.write(
      [
        verdict,
        `scenario=${row.slug}`,
        `original=${row.proof_url}`,
        `reproduced=${outcome.proofUrl}`,
        `signature=${sig}`,
      ].join("\t") + "\n",
    );
  }
  return reproduced ? 0 : EXIT.GENERIC;
}
