#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///

# ─── How to run ───
# 1. Install uv: curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. Use through: uv run python scripts/run_nutricoach_v140_golden_path.py --help
# ──────────────────

"""Replay every observed production boundary with one-shot process exit."""

from __future__ import annotations

import json
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

from scripts.nutricoach_v140_golden_path_fault_outcomes import (
    derive_fault_outcome, snapshot_files, snapshot_lines,
)
from scripts.nutricoach_v140_golden_path_faults import read_boundary_events
from scripts.nutricoach_v140_golden_path_models import CliError, FaultResult, StepRequest
from scripts.nutricoach_v140_golden_path_orchestrator import scenario_steps
from scripts.nutricoach_v140_golden_path_profile import create_profile
from scripts.nutricoach_v140_golden_path_transport import read_provider_calls

ENTRYPOINT = Path(__file__).with_name("run_nutricoach_v140_golden_path.py")
def _child(root: Path, request: StepRequest, evidence: Path) -> int:
    completed = subprocess.run(
        [
            sys.executable, "-B", str(ENTRYPOINT), "--child-step", request.model_dump_json(),
            "--root", str(root), "--process-evidence", str(evidence),
        ],
        check=False, capture_output=True, text=True,
    )
    if completed.returncode not in {0, 86}:
        _ = (root / "last-child-error.txt").write_text(completed.stderr, encoding="utf-8")
    return completed.returncode


def _targets(root: Path) -> tuple[tuple[int, StepRequest, str, str], ...]:
    steps = scenario_steps()
    index_by_step = {step.step_id: index for index, step in enumerate(steps)}
    selected: dict[str, tuple[int, StepRequest, str, str]] = {}
    occurrences: dict[tuple[str, str], int] = {}
    weekly_members = (
        "customer_schedule.py", "weekly_operations_store.py",
        "nutrition_weekly_operations_ledger_history.py",
        "nutrition_weekly_owner_ledger.py",
    )
    for event in read_boundary_events(root):
        if not (
            event.boundary.startswith("provider:")
            or any(member in event.boundary for member in weekly_members)
        ):
            continue
        index = index_by_step[event.step_id]
        occurrence_key = event.step_id, event.boundary
        occurrence = occurrences.get(occurrence_key, 0) + 1
        occurrences[occurrence_key] = occurrence
        candidate = index, steps[index], event.boundary, f"{event.boundary}#{occurrence}"
        current = selected.get(event.boundary)
        if current is None or index < current[0] or (
            index == current[0] and event.step_id == current[1].step_id
        ):
            selected[event.boundary] = candidate
    return tuple(selected[key] for key in sorted(selected))


def run_fault_matrix(root: Path) -> list[FaultResult]:
    """Crash after each observed seam, restart, and derive its durable outcome."""
    results: list[FaultResult] = []
    for ordinal, (target_index, target, boundary, failpoint) in enumerate(
        _targets(root), start=1,
    ):
        fault_parent = Path(tempfile.mkdtemp(prefix=f"nutricoach-t10-fault-{ordinal:02d}-"))
        fault_root = fault_parent / "profile"
        try:
            create_profile(fault_root)
            for prefix_index, request in enumerate(scenario_steps()[:target_index], start=1):
                code = _child(
                    fault_root, request,
                    fault_root / "processes" / f"prefix-{prefix_index:03d}.json",
                )
                if code != 0:
                    raise CliError(f"fault prefix failed: {request.step_id}:{code}")
            before = snapshot_lines(fault_root)
            prior_files = snapshot_files(fault_root)
            injected = target.model_copy(update={"failpoint": failpoint})
            crash_code = _child(
                fault_root, injected, fault_root / "processes" / "injected.json"
            )
            if crash_code != 86:
                raise CliError(f"fault was not observed: {boundary}:{crash_code}")
            restart_code = _child(
                fault_root, target, fault_root / "processes" / "restart.json"
            )
            if restart_code != 0:
                detail = (fault_root / "last-child-error.txt").read_text(encoding="utf-8")
                raise CliError(f"fault restart failed: {boundary}:{restart_code}:{detail}")
            calls = read_provider_calls(fault_root / "provider-transcript.jsonl")
            duplicates = len(calls) - len({call.call_key for call in calls})
            if duplicates:
                raise CliError(f"fault duplicated provider authority: {boundary}")
            outcome, evidence = derive_fault_outcome(
                fault_root, before, prior_files, boundary, target,
            )
            results.append(FaultResult(
                boundary=boundary, step_id=target.step_id,
                crash_exit=crash_code, restart_exit=restart_code,
                duplicate_provider_calls=duplicates, outcome=outcome, evidence=evidence,
            ))
        finally:
            shutil.rmtree(fault_parent)
    output = root / "fault-matrix.json"
    _ = output.write_text(
        json.dumps(
            [result.model_dump(mode="json") for result in results],
            sort_keys=True, separators=(",", ":"),
        ) + "\n",
        encoding="utf-8",
    )
    output.chmod(0o600)
    return results
