"""Run the V14 transaction matrix on an exact two-row live-profile clone."""

from __future__ import annotations

import hashlib
import json
import shutil
import sys
from pathlib import Path

from pydantic import JsonValue, TypeAdapter

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

from scripts.nutricoach_v150_concrete_host import ConcreteLiveHost
from scripts.nutricoach_v150_live_upgrade_common import canonical
from scripts.nutricoach_v150_sealed_controller import (
    SealedControllerError,
    execute_disposable,
)
from scripts.nutricoach_v150_sealed_target import (
    DisposableService,
    HostPaths,
)

_OBJECT = TypeAdapter(dict[str, JsonValue])
LIVE = Path("/home/cube/.hermes/profiles/dualcoachtest")
BASE = Path("/home/cube/.hermes/migrations/nutricoach-v1.5.0-combined")
PRESEAL = BASE / "live-transaction-preseal-v14-live-representative-r20"
EVIDENCE = Path(
    "/home/cube/projects/richard/traning coach/.omo/evidence/"
    + "nutricoach-v150-combined/task-v14r20-rehearsal"
)
CANDIDATE = "3dab1a5531c71239899c5a836f8689b9b6c59dc07ca7443de76442cf0eb5090d"
LIVE_CURRENT = LIVE / ".strict-runtime/6c9c4394-v132/venv"
UNIT = Path.home() / ".config/systemd/user/hermes-gateway-dualcoachtest.service"
DROPIN = (
    Path.home()
    / (
        ".config/systemd/user/hermes-gateway-dualcoachtest.service.d/"
        + "task26-authority.conf"
    )
)
STAGES = (
    "stop",
    "post_stop_snapshot",
    "stopped_probe",
    "install_exact_wheels",
    "off_smoke_channel_inbox_off",
    "capacity_dry_run",
    "weekly_and_capacity_apply",
    "weekly_startup_smoke",
    "switch_unit_dropin",
    "reload",
    "start",
    "post_fence",
)


def _load(path: Path) -> dict[str, JsonValue]:
    return _OBJECT.validate_json(path.read_bytes())


def _sha(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def _tree_digest(root: Path) -> str:
    rows = [
        f"{path.relative_to(root).as_posix()}:{_sha(path)}"
        for path in root.rglob("*")
        if path.is_file() and not path.is_symlink()
    ]
    return hashlib.sha256("\n".join(sorted(rows)).encode()).hexdigest()


def _remap_profile_path(value: JsonValue | None, profile: Path) -> JsonValue | None:
    if not isinstance(value, str):
        return value
    source = Path(value)
    if not source.is_relative_to(LIVE):
        return value
    return str(profile / source.relative_to(LIVE))


def _rebind_activation_authority(profile: Path) -> None:
    journal_path = profile / "data/customer-activation-journal.json"
    audit_path = profile / "data/customer-activation-audit.jsonl"
    journal = _load(journal_path)
    audit = _load(audit_path)
    path_fields = (
        "registry_path",
        "data_root",
        "audit_path",
        "staff_membership_evidence_path",
    )
    for field in path_fields:
        journal[field] = _remap_profile_path(journal.get(field), profile)
        audit[field] = _remap_profile_path(audit.get(field), profile)
    journal["audit_record_sha256"] = hashlib.sha256(canonical(audit)).hexdigest()
    _ = journal_path.write_text(
        json.dumps(journal, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    _ = audit_path.write_bytes(canonical(audit) + b"\n")


def _clone(case_root: Path, fault: str | None) -> ConcreteLiveHost:
    profile = case_root / "profile"
    profile.mkdir(parents=True)
    (profile / "customers").mkdir()
    _ = shutil.copy2(LIVE / "customers/registry.json", profile / "customers/registry.json")
    _ = shutil.copy2(LIVE / "config.yaml", profile / "config.yaml")
    _ = shutil.copytree(LIVE / "data", profile / "data", copy_function=shutil.copy2)
    _rebind_activation_authority(profile)
    current = profile / ".strict-runtime/current/venv"
    (current / "bin").mkdir(parents=True)
    _ = (current / "bin/python").write_text("representative predecessor\n")
    unit = case_root / "gateway.service"
    dropin = case_root / "authority.conf"
    _ = unit.write_bytes(
        UNIT.read_bytes().replace(str(LIVE_CURRENT).encode(), str(current).encode())
    )
    _ = dropin.write_bytes(
        DROPIN.read_bytes().replace(str(LIVE_CURRENT).encode(), str(current).encode())
    )
    target = _load(PRESEAL / "sealed-target.json")
    successor = target.get("successor_runtime")
    if not isinstance(successor, str):
        raise RuntimeError("successor_runtime")
    paths = HostPaths(
        profile,
        profile / "customers/registry.json",
        profile / "config.yaml",
        unit,
        dropin,
        current,
        profile / ".strict-runtime" / Path(successor).parent.name / "venv",
        case_root / "execution",
        case_root / "ledger",
    )
    wheels = target.get("wheels")
    if not isinstance(wheels, list) or len(wheels) != 2:
        raise RuntimeError("wheels")
    hermes = wheels[0]
    package = wheels[1]
    if not isinstance(hermes, dict) or not isinstance(package, dict):
        raise RuntimeError("wheel_rows")
    return ConcreteLiveHost(
        paths,
        DisposableService(),
        live=True,
        candidate_digest=CANDIDATE,
        hermes_wheel=Path(str(hermes["path"])),
        hermes_wheel_sha256=str(hermes["sha256"]),
        profile_wheel=Path(str(package["path"])),
        profile_wheel_sha256=str(package["sha256"]),
        dependency_snapshot=Path(str(target["dependency_snapshot"])),
        dependency_snapshot_sha256=str(target["dependency_snapshot_sha256"]),
        fault=ValueError(fault) if fault is not None else None,
        fault_stage=fault or "install_exact_wheels",
    )


def _mutable(host: ConcreteLiveHost) -> tuple[bytes, ...]:
    return tuple(path.read_bytes() for path in host.paths.mutable)


def _registry_assertions(host: ConcreteLiveHost) -> None:
    registry = _load(host.paths.registry)
    customers = registry.get("customers")
    policy = registry.get("admission_policy")
    if not isinstance(customers, list) or not isinstance(policy, dict):
        raise RuntimeError("registry_postimage")
    enabled = [
        row.get("customer_key")
        for row in customers
        if isinstance(row, dict) and row.get("enabled") is True
    ]
    disabled = [
        row.get("customer_key")
        for row in customers
        if isinstance(row, dict) and row.get("enabled") is not True
    ]
    if (
        len(customers) != 2
        or enabled != ["pilot_20260820_01"]
        or disabled != ["task26_claim_20260818145508_1b96b23d"]
        or policy.get("candidate_digest") != CANDIDATE
    ):
        raise RuntimeError("registry_projection")
    config = host.paths.config.read_text(encoding="utf-8")
    config_rows = {line.strip() for line in config.splitlines()}
    if (
        f"candidate_digest: {CANDIDATE}" not in config_rows
        or "- pilot_20260820_01" not in config_rows
        or "task26_claim_20260818145508_1b96b23d" in config
    ):
        raise RuntimeError("weekly_projection")


def main() -> int:
    if EVIDENCE.exists():
        raise RuntimeError("rehearsal_exists")
    EVIDENCE.mkdir(parents=True)
    live_before = {
        "registry": _sha(LIVE / "customers/registry.json"),
        "config": _sha(LIVE / "config.yaml"),
        "data": _tree_digest(LIVE / "data"),
        "unit": _sha(UNIT),
        "dropin": _sha(DROPIN),
    }
    rows: list[JsonValue] = []
    target = _load(PRESEAL / "sealed-target.json")
    package_path = target.get("permission_package")
    approval = target.get("approval_phrase")
    if not isinstance(package_path, str) or not isinstance(approval, str):
        raise RuntimeError("target_authority")
    package = _load(Path(package_path))
    success_root = EVIDENCE / "success"
    success = _clone(success_root, None)
    receipt = execute_disposable(
        approval,
        success_root,
        success,
        expected_approval=approval,
    )
    _registry_assertions(success)
    if (
        success.stages != list(STAGES)
        or not success.service.running
        or success.network_events
        or success.telegram_events
        or success.provider_events
    ):
        raise RuntimeError("success_postcondition")
    rows.append({
        "case": "success",
        "receipt": receipt,
        "stages": list(success.stages),
        "status": "PASS",
    })
    for stage in STAGES:
        case_root = EVIDENCE / f"rollback-{stage}"
        host = _clone(case_root, stage)
        before = _mutable(host)
        try:
            _ = execute_disposable(
                approval,
                case_root,
                host,
                expected_approval=approval,
            )
        except ValueError as error:
            if str(error) != stage:
                raise
        else:
            raise RuntimeError(f"fault_not_observed:{stage}")
        if (
            _mutable(host) != before
            or not host.service.running
            or not host.ledger_consumed()
            or host.successor_root.exists()
            or host.weekly_authority.exists()
        ):
            raise RuntimeError(f"rollback_postcondition:{stage}")
        try:
            _ = execute_disposable(
                approval,
                case_root,
                host,
                expected_approval=approval,
            )
        except SealedControllerError as error:
            if "already_used" not in str(error):
                raise
        else:
            raise RuntimeError(f"replay_allowed:{stage}")
        rows.append({"case": f"rollback:{stage}", "status": "PASS"})
    live_after = {
        "registry": _sha(LIVE / "customers/registry.json"),
        "config": _sha(LIVE / "config.yaml"),
        "data": _tree_digest(LIVE / "data"),
        "unit": _sha(UNIT),
        "dropin": _sha(DROPIN),
    }
    if live_after != live_before:
        raise RuntimeError("live_source_mutated")
    report = _OBJECT.validate_python({
        "schema": "nutricoach-v150-v14-live-shaped-rehearsal",
        "candidate_digest": CANDIDATE,
        "package_digest": package["package_digest"],
        "network_namespace": "loopback-only",
        "live_source_unchanged": True,
        "registry_shape": {
            "customer_count": 2,
            "enabled_customer_keys": ["pilot_20260820_01"],
            "disabled_customer_keys": ["task26_claim_20260818145508_1b96b23d"],
        },
        "cases": rows,
        "case_count": len(rows),
        "status": "PASS",
    })
    _ = (EVIDENCE / "report.json").write_bytes(canonical(report) + b"\n")
    print(json.dumps(report, sort_keys=True))
    return 0


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