#!/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. Generate: uv run python scripts/run_nutricoach_v140_golden_path.py --root /tmp/nc --output /tmp/receipt.json --manifest /tmp/manifest.json --network-disabled
# 3. Verify: uv run python scripts/run_nutricoach_v140_golden_path.py --verify --root /tmp/nc --receipt /tmp/receipt.json --manifest /tmp/manifest.json
# ──────────────────

"""Todo10 r2 production Golden Path generation and verification CLI."""

from __future__ import annotations

import getopt
import json
import os
import sys
from collections.abc import Mapping, Sequence
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Final
from zoneinfo import ZoneInfo

import anyio

REPOSITORY: Final = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPOSITORY / "dualcoach" / "profile"))
sys.path.insert(0, str(REPOSITORY))

from scripts.nutricoach_v140_golden_path_fault_matrix import run_fault_matrix  # noqa: E402
from gateway.platforms.nutrition_weekly_owner_model import JsonValue  # noqa: E402
from scripts.nutricoach_v140_golden_path_models import (  # noqa: E402
    CliError, ProviderMode, StepRequest,
)
from scripts.nutricoach_v140_golden_path_orchestrator import run_scenario  # noqa: E402
from scripts.nutricoach_v140_golden_path_receipt import build_receipt  # noqa: E402
from scripts.nutricoach_v140_golden_path_steps import execute_step  # noqa: E402
from scripts.nutricoach_v140_golden_path_transport import DurableFakeTelegram  # noqa: E402
from scripts.nutricoach_v140_golden_path_privacy import SENTINELS  # noqa: E402
from scripts.nutricoach_v140_golden_path_verifier import (  # noqa: E402
    ArtifactMismatch, build_manifest, verify_artifacts,
)

USAGE: Final = """usage:
  generate: --root PATH --output PATH --manifest PATH --network-disabled
  verify:   --verify --root PATH --receipt PATH --manifest PATH
  help:     --help
"""


@dataclass(frozen=True, slots=True)
class Parsed:
    values: dict[str, str]
    flags: frozenset[str]


def _parse(argv: Sequence[str]) -> Parsed:
    try:
        options, positional = getopt.gnu_getopt(
            argv, "", (
                "root=", "output=", "receipt=", "manifest=", "network-disabled",
                "verify", "help", "child-step=", "process-evidence=", "skip-fault-matrix",
                "privacy-negative",
            ),
        )
    except getopt.GetoptError as error:
        raise CliError(str(error)) from error
    if positional:
        raise CliError("positional arguments are not accepted")
    values: dict[str, str] = {}
    flags: set[str] = set()
    for option, value in options:
        name = option.removeprefix("--")
        if value:
            values[name] = value
        else:
            flags.add(name)
    return Parsed(values, frozenset(flags))


def _required(parsed: Parsed, name: str) -> Path:
    value = parsed.values.get(name)
    if value is None:
        raise CliError(f"--{name} is required")
    return Path(value)


def _atomic_json(path: Path, document: Mapping[str, JsonValue]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    temporary = path.with_name(f".{path.name}.tmp")
    encoded = json.dumps(document, sort_keys=True, separators=(",", ":")) + "\n"
    with temporary.open("w", encoding="utf-8") as stream:
        _ = stream.write(encoded)
        stream.flush()
        os.fsync(stream.fileno())
    os.replace(temporary, path)


def _child(parsed: Parsed) -> int:
    root = _required(parsed, "root")
    evidence = _required(parsed, "process-evidence")
    raw = parsed.values.get("child-step")
    if raw is None:
        raise CliError("--child-step is required")
    return execute_step(root, StepRequest.model_validate_json(raw), evidence)


def _verify(parsed: Parsed) -> int:
    verify_artifacts(
        _required(parsed, "root"), _required(parsed, "receipt"),
        _required(parsed, "manifest"),
    )
    print("NUTRICOACH_V140_GOLDEN_PATH_VERIFY_PASS")
    return 0


def _generate(parsed: Parsed) -> int:
    if "network-disabled" not in parsed.flags:
        raise CliError("--network-disabled is required")
    root = _required(parsed, "root")
    output = _required(parsed, "output")
    manifest_path = _required(parsed, "manifest")
    if root.exists():
        raise CliError("--root must not exist")
    _ = run_scenario(root)
    if "skip-fault-matrix" in parsed.flags:
        _ = (root / "fault-matrix.json").write_text("[]\n", encoding="utf-8")
    else:
        _ = run_fault_matrix(root)
    receipt = build_receipt(root)
    _atomic_json(output, receipt)
    _atomic_json(manifest_path, build_manifest(root, output))
    verify_artifacts(root, output, manifest_path)
    print("NUTRICOACH_V140_GOLDEN_PATH_PASS")
    return 0


async def _append_privacy_negative(root: Path) -> None:
    provider = DurableFakeTelegram(
        root / "provider-transcript.jsonl",
        datetime(2026, 8, 24, 9, tzinfo=ZoneInfo("Asia/Seoul")), ProviderMode.DELIVERED,
    )
    _ = await provider.send_message(
        "1", f"negative fixture {SENTINELS['free_text']}", message_thread_id=41,
    )


def _privacy_negative(parsed: Parsed) -> int:
    anyio.run(_append_privacy_negative, _required(parsed, "root"))
    return 0


def main() -> int:
    try:
        parsed = _parse(sys.argv[1:])
        if "help" in parsed.flags:
            print(USAGE)
            return 0
        if "child-step" in parsed.values:
            return _child(parsed)
        if "verify" in parsed.flags:
            return _verify(parsed)
        if "privacy-negative" in parsed.flags:
            return _privacy_negative(parsed)
        return _generate(parsed)
    except CliError as error:
        print(f"{USAGE}\nerror: {error}", file=sys.stderr)
        return 2
    except ArtifactMismatch as error:
        print(str(error), file=sys.stderr)
        return 1


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