#!/usr/bin/env python3
"""Run a deterministic CARBON checkout-recovery story in the real live viewer."""

from __future__ import annotations

import argparse
import json
import os
import secrets
import subprocess
import sys
import time
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
RUNTIME = ROOT / "runtime" / "carbon" / "scripts" / "carbon_command_progress.py"


def runtime(action: str, project: Path, *, progress_id: str = "", payload: dict | None = None) -> dict:
    command = ["python3", str(RUNTIME), action, "--root", str(project)]
    if progress_id:
        command += ["--progress-id", progress_id]
    if payload is not None:
        command += ["--payload-json", json.dumps(payload)]
    completed = subprocess.run(command, check=True, text=True, capture_output=True)
    return json.loads(completed.stdout)


def wait_for_ready(path: Path) -> dict:
    for _ in range(80):
        try:
            value = json.loads(path.read_text(encoding="utf-8"))
            if value.get("baseUrl"):
                return value
        except (OSError, json.JSONDecodeError):
            pass
        time.sleep(0.1)
    raise RuntimeError("CARBON demo viewer did not start")


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--root", default=str(ROOT / "output" / "demo" / "checkout-recovery"), help="Existing or new demo project directory")
    parser.add_argument("--interval", type=float, default=2.4, help="Seconds between story stages")
    parser.add_argument("--viewer-ttl", type=int, default=900, help="Viewer lifetime after launch")
    args = parser.parse_args()
    project = Path(args.root).expanduser().resolve()
    project.mkdir(parents=True, exist_ok=True)

    started = runtime("start", project, payload={
        "command": "carbon-test",
        "title": "Test checkout recovery after a payment API change",
        "scope": "Deterministic local demonstration · no real payment or production account",
        "reason": "The change crosses payment status, cart persistence, and retry state; another happy-path checkout would not exercise that recovery branch.",
        "steps": [
            {"id": "map", "title": "Map the changed recovery path", "detail": "Trace payment confirmation, cart cleanup, and retry state.", "why": "The diff touches three components that must converge after a timeout.", "risk": "state divergence"},
            {"id": "rank", "title": "Rank the affected and adjacent risks", "detail": "Compare cart loss, duplicate payment, and cosmetic error behavior.", "why": "Lost money or user state carries more consequence than another successful checkout.", "risk": "duplicate payment"},
            {"id": "plan", "title": "Select the smallest useful failure condition", "detail": "Delay the payment response after authorization without charging a real card.", "why": "The timing window sits exactly between the changed response and cleanup logic.", "risk": "lost cart"},
            {"id": "execute", "title": "Force the timeout and observe recovery", "detail": "Capture UI, network, and stored cart state from a known local baseline.", "why": "Only observed behavior can establish whether recovery works.", "risk": "lost cart"},
            {"id": "verify", "title": "Verify the repair and preserve uncertainty", "detail": "Repeat the original failure and retain the next untested risk.", "why": "A code edit is not proof until the same failure condition passes.", "risk": "retry idempotency"},
        ],
        "confidence": {"start": 42, "current": 42, "label": "Source context only", "basis": "The change is mapped, but recovery behavior has not been executed."},
        "risks": [
            {"id": "cart", "label": "Cart loss after timeout", "detail": "Cleanup may run before payment status is known.", "severity": "high", "status": "open"},
            {"id": "duplicate", "label": "Duplicate payment on retry", "detail": "Idempotency behavior is not yet observed.", "severity": "critical", "status": "unknown"},
        ],
        "metrics": [{"label": "Affected surface", "value": "3 components", "detail": "UI, payment API, cart state"}],
    })
    progress_id = started["progressId"]
    runtime_dir = project / ".carbon" / "runtime"
    runtime_dir.mkdir(parents=True, exist_ok=True)
    ready = runtime_dir / f"demo-viewer-{secrets.token_hex(6)}.json"
    token = secrets.token_urlsafe(32)
    env = {
        "PATH": os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin"),
        "LANG": os.environ.get("LANG", "C.UTF-8"),
        "LC_ALL": os.environ.get("LC_ALL", "C"),
        "CARBON_COMMAND_VIEW_TOKEN": token,
    }
    subprocess.Popen(
        ["python3", str(RUNTIME), "serve", "--root", str(project), "--progress-id", progress_id, "--ready-file", str(ready), "--ttl-seconds", str(max(60, args.viewer_ttl))],
        cwd=project,
        env=env,
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        start_new_session=True,
    )
    viewer = wait_for_ready(ready)
    print(f"CARBON_DEMO_URL={viewer['baseUrl']}/command#{token}", flush=True)
    print("Open that URL inside the coding-agent side view. The story now advances automatically.", flush=True)

    stages = [
        {"phase": "Ranking risk", "summary": "Cart loss and duplicate payment outrank another happy-path checkout.", "steps": [{"id": "map", "status": "completed", "summary": "Payment, cart, and retry branches mapped."}], "advance": True, "confidence": {"start": 42, "current": 46, "label": "Risk mapped", "basis": "The change now has explicit consequence and consumer context."}, "risks": [{"id": "cart", "label": "Cart loss after timeout", "detail": "Cleanup may run before payment status is known.", "severity": "high", "status": "testing"}, {"id": "duplicate", "label": "Duplicate payment on retry", "detail": "Idempotency behavior is not yet observed.", "severity": "critical", "status": "unknown"}]},
        {"phase": "Building the test", "summary": "A safe local timeout scenario is ready; no product result is claimed yet.", "steps": [{"id": "rank", "status": "completed", "summary": "Recovery failures ranked by consequence."}], "advance": True, "confidence": {"start": 42, "current": 48, "label": "Traceable plan", "basis": "The highest-ranked risk now has an executable local test."}, "evidence": [{"id": "risk-map", "label": "Recovery risk map", "detail": "Three affected branches linked to the changed code.", "type": "lineage", "status": "captured"}]},
        {"phase": "Forcing the timeout", "summary": "CARBON is delaying the payment response and collecting UI, network, and cart state.", "steps": [{"id": "plan", "status": "completed", "summary": "Bounded local failure condition prepared."}], "advance": True, "confidence": {"start": 42, "current": 48, "label": "Execution in progress", "basis": "No outcome is recorded until the resulting state is observed."}, "metrics": [{"label": "Current action", "value": "Payment timeout", "detail": "After authorization"}, {"label": "Evidence channels", "value": "3", "detail": "UI, network, cart state"}]},
        {"phase": "Failure observed", "summary": "The error message appeared, but the cart was emptied before payment status was known.", "steps": [{"id": "execute", "status": "failed", "summary": "0 of 3 cart items remained; retry returned to product search."}], "advance": True, "confidence": {"start": 42, "current": 38, "label": "Recovery failed", "basis": "Observed evidence reduced confidence in checkout recovery."}, "risks": [{"id": "cart", "label": "Cart loss after timeout", "detail": "Observed: all three items were removed.", "severity": "high", "status": "open"}, {"id": "duplicate", "label": "Duplicate payment on retry", "detail": "Idempotency behavior is not yet observed.", "severity": "critical", "status": "unknown"}], "evidence": [{"id": "failure", "label": "Empty cart after timeout", "detail": "Observed browser state and matching delayed API response.", "type": "screenshot + trace", "status": "failed"}]},
        {"phase": "Repair verified", "summary": "The original timeout now preserves all three items and the recovery action.", "status": "partial", "steps": [{"id": "verify", "status": "completed", "summary": "Original failure reran successfully from the same starting state."}], "confidence": {"start": 42, "current": 68, "label": "Repair verified", "basis": "The original failure passed on rerun; retry idempotency remains untested."}, "risks": [{"id": "cart", "label": "Cart loss after timeout", "detail": "The exact failure passed on rerun.", "severity": "high", "status": "mitigated"}, {"id": "duplicate", "label": "Duplicate payment on retry", "detail": "Server-side idempotency still needs direct evidence.", "severity": "critical", "status": "unknown"}], "evidence": [{"id": "failure", "label": "Original empty-cart failure", "detail": "Reproduced before the repair.", "type": "screenshot + trace", "status": "failed"}, {"id": "rerun", "label": "Passing recovery rerun", "detail": "3 of 3 items retained and recovery action visible.", "type": "screenshot + trace", "status": "passed"}], "metrics": [{"label": "Observed outcome", "value": "3 of 3 retained", "detail": "Original failure verified"}, {"label": "Remaining unknown", "value": "Idempotency", "detail": "Not assessed"}]},
    ]
    for payload in stages:
        time.sleep(max(0.2, args.interval))
        runtime("update", project, progress_id=progress_id, payload=payload)
    print("CARBON demo complete. The protected page remains available until its viewer TTL expires.", flush=True)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
