/**
 * # cli/commands/prove — `prove` (LIGHT, node-runnable): the zero-credential front door (n04e.1).
 *
 * `npx kestrel.markets prove` with NO args, NO key, NO config, and NO prompt must SUCCEED: it
 * selects a pinned default free scenario, runs the same hosted mint → simulate → proof flow the
 * `sim` verb drives (against the labeled stand-down baseline, author-no-strategy), prints the
 * graded story + the shareable proof URL, and — this is its only behavioral delta vs `sim` —
 * exhausts to the machine-parseable RESIDENCY snippet addressed to the agent that just ran it.
 *
 *   - bare `prove`               → the pinned default scenario (never a menu, never a prompt).
 *   - `prove <lowercase-slug>`   → that curated scenario.
 *   - `prove --plans <file>`     → author a real strategy for the run.
 *
 * LIGHT by construction: it reuses `sim.ts`'s hosted core over fetch only (no bun/chdb), so the
 * front-door snippet runs under plain node via npx. It always routes to the hosted funnel (a
 * proof NAMES platform data), the default base overridable by `--api`/`KESTREL_API`.
 *
 * BOUNDARY: like `sim`, `prove` is a THIN HTTP client of the public `api.kestrel.markets` — it
 * embeds no platform business logic. The opaque `--copy-token` it forwards is an uninterpreted
 * pass-through nonce (see {@link ../backend/hosted.ts HostedFunnel.mintTrial}); all attribution
 * meaning lives in the managed platform.
 */

import { HostedFunnel, resolveScenarioSlug, nearMissSlugs, slugifyTitle, type HostedCatalogEntry, type ResolvedScenario } from "../backend/hosted.ts";
import { defaultHostedStorePath } from "../../ledger/hosted.ts";
import type { OutputCtx, GlobalFlags } from "../context.ts";
import { parseArgs } from "../args.ts";
import { CliError, EXIT } from "../errors.ts";
import {
  resolveSimBase,
  loadStrategy,
  resolveCopyToken,
  parseSimSelector,
  runHostedProof,
} from "./sim.ts";

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

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

/**
 * The pinned default scenario a bare `prove` runs. A stable, all-free catalog slug so the
 * zero-credential front door is deterministic across machines (site-is-spec). If the live
 * catalog no longer carries it, {@link selectDefaultScenario} falls back to the first free
 * entry — and fails CLOSED (never a menu) if the catalog offers none.
 *
 * MUST be a tape the bundled sampler starter actually FILLS on (kestrel-etvn): the starter
 * rests a limit that fills only when price reverts back through the entry. The mean-reversion
 * range-fade tape reverts through the open, so the resting limit crosses and the run certifies
 * a real, filled trade (order_count=1 fill_count=1 realized_pnl=-2 — an honest small loss).
 * An UP-ONLY tape (the prior `meme-stock-short-squeeze` default) never fills, which is how the
 * bare front door came to certify an empty session. Aligned with the platform quickstart
 * (kestrel.markets PR #328) — same RNGE tape, same starter shape, same graded result.
 */
export const DEFAULT_PROVE_SLUG = "mean-reversion-range-fade";

/**
 * The BUNDLED sampler-starter plan a bare `prove` runs when the caller supplies no `--plans`.
 * Deliberately minimal — buy one share at the open and hold to the risk-envelope `ttl` — and
 * clearly a REPLACEABLE EXAMPLE (never a hidden strategy): it is exactly parallel to what
 * `--plans` uploads, so author-provenance stays customer/explicitly-supplied server-side.
 * On the range-fade tape ({@link DEFAULT_PROVE_SLUG}) this resting limit fills, so the bare
 * front door certifies a real trade instead of an empty session. Same three lines the platform
 * quickstart authors (kestrel.markets PR #328). Never fabricated: the grade is the honest,
 * filled result of THIS plan on THIS tape.
 */
export const SAMPLER_STARTER_DOC = "PLAN starter budget 1R ttl +24h\n  WHEN spot > 0\n  DO buy 1 shares @ spot";

/** The self-describing label for the bundled sampler starter, printed as the strategy label. */
export const SAMPLER_STARTER_LABEL = "sampler starter (bundled example — supply --plans <file> to author your own)";

/** The swap-in re-entry the one-frame lesson points a reader at: author your OWN plan. */
const SWAP_IN_CLI = "npx kestrel.markets prove --plans <your-plan.kestrel>";

/**
 * Select the scenario a BARE `prove` runs — fail-closed. Bare prove must NEVER fall through to
 * a menu or a prompt (that is the inert-front-door failure), so an empty selection is a loud
 * refusal, never a silent default. Preference: the pinned {@link DEFAULT_PROVE_SLUG}, else the
 * first `free:true` catalog entry.
 */
export function selectDefaultScenario(entries: readonly HostedCatalogEntry[]): ResolvedScenario {
  // The front door is FREE by construction — the pinned default is used only when it is a free
  // catalog entry; otherwise fall through to the first free one (never a paid default that would
  // dead-end at a 402).
  const pinned = resolveScenarioSlug(DEFAULT_PROVE_SLUG, entries);
  if (pinned !== null && pinned.entry.free) return pinned;
  const firstFree = entries.find((e) => e.free);
  if (firstFree === undefined)
    throw new CliError({
      code: "NOT_FOUND",
      exit: EXIT.NOT_FOUND,
      message: "no free scenario is available for `prove` — the hosted catalog offered none",
      hint: "run `kestrel sim` to see the catalog, or name a scenario: `kestrel prove <slug>`",
    });
  const slug = slugifyTitle(firstFree.title);
  return { entry: firstFree, canonicalSlug: slug, requestedSlug: slug, viaAlias: false };
}

export interface ProveDeps {
  /** Injected fetch (tests pass an in-process funnel face); defaults to global fetch. */
  readonly fetch?: FetchLike;
  /** Injected env (tests); defaults to process.env. */
  readonly env?: Env;
  /** Where the hosted-run receipt is appended; defaults to the identity-bound home store
   * `~/.kestrel/hosted-runs.jsonl` ({@link defaultHostedStorePath}, kestrel-fq78). */
  readonly hostedStorePath?: string;
  /**
   * Override the bare-scenario selector (tests only). The wiring guard: a stub that returns
   * `undefined` must make bare `prove` go RED (a loud refusal), NOT fall through to a menu.
   */
  readonly selectDefault?: (entries: readonly HostedCatalogEntry[]) => ResolvedScenario | undefined;
}

/**
 * `prove` — the zero-credential front door. Bare = the pinned default scenario + the residency
 * exhaust; a slug runs that scenario; `--plans` authors a strategy; `--no-residency` suppresses
 * the snippet; `--copy-token`/`KESTREL_COPY_TOKEN` forwards an opaque nonce to the mint.
 */
export async function proveCommand(
  argv: readonly string[],
  ctx: OutputCtx,
  globals: GlobalFlags,
  deps: ProveDeps = {},
): Promise<number> {
  const { positionals, flags, bools } = parseArgs(argv, new Set(["no-residency"]), new Set(["plans", "copy-token"]));
  const env = deps.env ?? (process.env as Env);
  const base = resolveSimBase(globals, env);
  const client = new HostedFunnel({ baseUrl: base, ...(deps.fetch !== undefined ? { fetch: deps.fetch } : {}) });

  const { entries } = await client.catalog();

  let resolved: ResolvedScenario;
  if (positionals.length === 0) {
    // Bare `prove` → the pinned default. FAIL CLOSED: never a menu, never a prompt.
    const select = deps.selectDefault ?? selectDefaultScenario;
    const picked = select(entries);
    if (picked === undefined)
      throw new CliError({
        code: "NOT_FOUND",
        exit: EXIT.NOT_FOUND,
        message: "`prove` could not select a default scenario — the front door refuses to fall through to a menu",
        hint: "name a scenario: `kestrel prove <slug>`, or run `kestrel sim` for the catalog",
      });
    resolved = picked;
  } else {
    // `prove <slug>` — the same case-rule as `sim` (reserved ticker/mixed/<when> forms refuse).
    const { scenarioSlug } = parseSimSelector(positionals);
    const match = resolveScenarioSlug(scenarioSlug, entries);
    if (match === null) {
      const near = nearMissSlugs(scenarioSlug, entries);
      throw new CliError({
        code: "NOT_FOUND",
        exit: EXIT.NOT_FOUND,
        message: `unknown scenario slug ${JSON.stringify(scenarioSlug)}`,
        hint:
          near.length > 0
            ? `did you mean: ${near.join(", ")}? — run \`kestrel sim\` for the full menu`
            : "run `kestrel sim` for the menu",
      });
    }
    resolved = match;
  }

  // Bare `prove` (no `--plans`) defaults to the BUNDLED sampler starter — a labeled, replaceable
  // example that actually ARMS an order, so the front door certifies a real trade rather than an
  // empty session. An author-supplied `--plans` always wins (the bundled default is consulted only
  // when `plansPath` is undefined), so the customer's own strategy path is unchanged.
  const plansPath = flags.get("plans");
  const usingBundledStarter = plansPath === undefined;
  const strategy = loadStrategy(plansPath, { source: SAMPLER_STARTER_DOC, label: SAMPLER_STARTER_LABEL });
  const copyToken = resolveCopyToken(flags, env);

  return runHostedProof({
    client,
    resolved,
    strategy,
    ctx,
    hostedStorePath: deps.hostedStorePath ?? defaultHostedStorePath(env),
    emitResidency: !bools.has("no-residency"),
    // Whether the caller authored a plan (`--plans`) vs the bundled sampler starter — governs the
    // `--copy-token` continuation notice (kestrel-gu9h): a copy-token run that did NOT author its own
    // plan cannot recover the origin plan from the opaque token, so it says so loudly instead of
    // silently running the bundled example.
    authoredPlan: !usingBundledStarter,
    // The one-frame lesson rides ONLY the bundled-starter path — never when the caller authored a plan.
    ...(usingBundledStarter
      ? { demoLesson: { planLabel: SAMPLER_STARTER_LABEL, planSource: strategy.source, swapInCli: SWAP_IN_CLI } }
      : {}),
    ...(copyToken !== undefined ? { copyToken } : {}),
  });
}
