#!/usr/bin/env python3
"""Read-only deterministic verifier for the Task26 live lifecycle seal."""
from __future__ import annotations

import argparse
import hashlib
import json
import os
import stat
import subprocess
from pathlib import Path
from typing import Any

import yaml

ROOT = Path(__file__).resolve().parent
PROFILE = Path("/home/cube/.hermes/profiles/dualcoachtest")
OTHER = Path("/home/cube/.hermes/profiles/physique-coach")
REPO = Path("/home/cube/projects/richard/hermes-agent")
CUSTOMER = "task26_live_2e_r2_20260815_8527916639"
TOKEN = "e7d63548ceecf2fd"
REVISION = "58f2cda7abd532c0d6e145bbb3f3216609b004374adab8bfae2ee28dc5d7d660"
CHECKIN = "96d6f8f1b5cfd02616ecd0845a39bbda1aa365a06f89cc6e7314d6fc86f245e7"
IDEMPOTENCY = "40f24646744df82f64a5e09ffb6a645e193ef929543137a841450793b6fa0f3f"


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


def load(path: Path) -> Any:
    return json.loads(path.read_text(encoding="utf-8"))


def tree_digest(root: Path) -> tuple[str, int]:
    rows: list[bytes] = []
    for path in sorted(root.rglob("*")):
        rel = path.relative_to(root).as_posix().encode()
        if path.is_symlink():
            rows.append(b"L\0" + rel + b"\0" + os.readlink(path).encode())
        elif path.is_dir():
            rows.append(b"D\0" + rel)
        elif path.is_file():
            rows.append(b"F\0" + rel + b"\0" + hashlib.sha256(path.read_bytes()).digest())
        else:
            raise AssertionError(f"unsupported profile entry: {path}")
    return hashlib.sha256(b"\n".join(rows)).hexdigest(), len(rows)


def assert_evidence(e: dict[str, Any]) -> None:
    assert e["schema"] == "task26-live-lifecycle-evidence-v1"
    assert e["status"] == "PASS_READY_FOR_SEPARATELY_AUTHORIZED_CLEANUP"
    c = e["candidate_binding"]
    assert c["full_candidate_digest"] == "4a6c7ee54cf9526a30de8bb576c1d71b411938beba33a04914738f6e1b6ed1cb"
    assert c["core_candidate_digest"] == "3374be765c7e53fc431b0c6e48cacbacb09618f77f13ef87016cb0ad2575d060"
    assert c["hermes_wheel_sha256"] == "9d22e89a0b1a14d4eb7f1c2005fc01860bec5f3b4eda6e01bb1390542a90b875"
    assert c["profile_wheel_sha256"] == "f75856d6d986b64d3d2f083aec2f865c7aea19f5950b84ff06e519f2f6505af6"
    assert c["deployment_receipt_sha256"] == "fe65b634396aaae2818719d1b34fdaad148b2547290d3a66b3af7803bf93dfca"
    assert c["prior_observer_v6_4_seal_sha256"] == "1de58545eba0f315e825372569fff35c1c2c3fc7552ecdb2d915b6c61daeb5c2"
    assert c["polling_recovery_seal_sha256"] == "00d44de03e8dde668da8058091751291b13457c8bd086917dfe85e42b3813df8"
    assert c["service_state_proof_sha256"] == "81c1e581cb84aac6f44044747e0e8e77a2d7498c71d626f1f9b5ec2f9d09a261"
    lifecycle = e["lifecycle"]
    assert lifecycle["activation"] == {
        "bootstrap_generation": 6, "bootstrap_state": "ACTIVE", "customer_enabled": True,
        "customer_key": CUSTOMER, "unique_target_bootstrap_sessions": 1,
    }
    assert lifecycle["checkin"]["flow"] == "nutrition_daily"
    assert lifecycle["checkin"]["revision"] == CHECKIN
    assert lifecycle["generation_and_approval"]["draft_id"] == TOKEN
    assert lifecycle["generation_and_approval"]["revision"] == REVISION
    assert lifecycle["generation_and_approval"]["owner_card_message_id"] == "231"
    assert lifecycle["delivery"]["idempotency_key"] == IDEMPOTENCY
    assert lifecycle["delivery"]["customer_message_id"] == "232"
    assert lifecycle["delivery"]["delivery_ledger_rows"] == 1
    assert lifecycle["no_duplicates"]["cron_jobs"] == 0
    a = e["manual_real_surface_attestation"]
    assert a["attestation_only"] and not a["customer_answers_included"] and not a["screenshot_bytes_included"]
    assert e["cleanup_authorization_preflight"]["current_execute_authorized"] is False


def verify_live(e: dict[str, Any]) -> dict[str, Any]:
    assert_evidence(e)
    for rel, expected in e["authorities"]["live_file_sha256"].items():
        assert sha(PROFILE / rel) == expected, rel
    cfg_path = PROFILE / "config.yaml"
    cfg = yaml.safe_load(cfg_path.read_text(encoding="utf-8"))
    adaptive = cfg["platforms"]["telegram"]["extra"]["adaptive_nutrition"]
    assert sha(cfg_path) == e["authorities"]["config"]["sha256"]
    assert stat.S_IMODE(cfg_path.stat().st_mode) == 0o664
    assert adaptive["enabled"] is True and adaptive["delivery_enabled"] is True
    assert adaptive["activation"] is False
    assert adaptive["delivery"] is False

    registry = load(PROFILE / "customers/registry.json")
    assert len(registry["customers"]) == 1
    assert registry["customers"][0]["customer_key"] == CUSTOMER and registry["customers"][0]["enabled"] is True
    bootstrap = load(PROFILE / "data/onboarding/telegram-customer-bootstrap-v1/ledger.json")["sessions"]
    target = [x for x in bootstrap if x["customer_draft"]["customer_key"] == CUSTOMER]
    assert len(target) == 1 and target[0]["generation"] == 6 and target[0]["state"] == "ACTIVE"

    final = load(PROFILE / "data/owner-actions/checkin-finalization-journal.json")["entries"]
    assert list(final) == [TOKEN]
    assert final[TOKEN]["state"] == "committed" and final[TOKEN]["customer_key"] == CUSTOMER
    generations = load(PROFILE / "data/owner-actions/draft-generations.json")
    assert list(generations) == [TOKEN]
    lineage = generations[TOKEN]
    expected_states = ["generation_pending", "generating", "draft_created", "approved", "delivery_pending", "delivered", "sent_audited"]
    assert [x["state"] for x in lineage] == expected_states
    assert all(x["checkin_revision"] == CHECKIN for x in lineage)
    assert [x.get("delivery_provider_receipt") for x in lineage if x["state"] in {"delivered", "sent_audited"}] == ["232", "232"]

    cards = load(PROFILE / "data/owner-actions/draft-generation-cards.json")
    assert list(cards) == [TOKEN]
    card = cards[TOKEN]
    assert card["message_id"] == "231" and card["state"] == "published" and card["draft_revision"] == REVISION
    drafts = load(PROFILE / "data/owner-actions/drafts.json")
    assert list(drafts) == [TOKEN]
    draft = drafts[TOKEN]
    assert draft["approved_revision"] == REVISION and draft["coach_review_state"] == "operator_approved" and draft["status"] == "sent"
    deliveries = load(PROFILE / "data/owner-actions/draft-deliveries.json")
    assert list(deliveries) == [f"{TOKEN}:{REVISION}"]
    delivery = deliveries[f"{TOKEN}:{REVISION}"]
    assert delivery["idempotency_key"] == IDEMPOTENCY and delivery["status"] == "sent_audited"
    assert delivery["message_id"] == "232" and delivery["provider_chat_id"] == "8527916639" and delivery["provider_topic_id"] == "0"
    assert delivery["text"] == draft["text"] and hashlib.sha256(delivery["text"].encode()).hexdigest() == REVISION
    assert not (PROFILE / "cron/jobs.json").exists()
    assert load(PROFILE / "data/onboarding/telegram-publication-outbox-v1/emergency.json")["records"] == []

    candidate = Path(".omo/evidence/task26/task26-customer-service-state-bootstrap-successor-4a6c7ee54cf9526a30de8bb576c1d71b411938beba33a04914738f6e1b6ed1cb")
    deploy = Path(".omo/evidence/task26/task26-certification-deployment-4a6c7ee54cf9526a30de8bb576c1d71b411938beba33a04914738f6e1b6ed1cb-st_01a00b39/receipts/deployment-receipt.json")
    observer = Path(".omo/evidence/task26/task26-continuous-lifecycle-observer-v6.4-4a6c7ee5/SEAL.json")
    recovery = Path(".omo/evidence/task26/task26-telegram-poll-recovery-st_01a00d2c/FINAL-SEAL.json")
    assert sha(candidate / "artifacts/hermes_agent-0.17.0-py3-none-any.whl") == e["candidate_binding"]["hermes_wheel_sha256"]
    assert sha(candidate / "candidate-manifest.json") == e["incident_successor_chain"]["current_successor_manifest_sha256"]
    assert sha(candidate / "candidate-seal.json") == e["incident_successor_chain"]["current_successor_seal_sha256"]
    assert sha(deploy) == e["candidate_binding"]["deployment_receipt_sha256"]
    assert sha(observer) == e["candidate_binding"]["prior_observer_v6_4_seal_sha256"]
    assert sha(recovery) == e["candidate_binding"]["polling_recovery_seal_sha256"]
    source_hash = e["candidate_binding"]["installed_and_source_module_sha256"]
    assert sha(REPO / "gateway/platforms/nutrition_coaching.py") == source_hash
    assert sha(REPO / ".venv/lib/python3.12/site-packages/gateway/platforms/nutrition_coaching.py") == source_hash
    assert sha(REPO / "tests/gateway/test_nutrition_coaching.py") == e["candidate_binding"]["source_test_sha256"]

    active_roots = [PROFILE / "customers", PROFILE / "data/owner-actions", PROFILE / "data/onboarding", PROFILE / "data/customers" / CUSTOMER]
    files = sorted(p for root in active_roots for p in root.rglob("*") if p.is_file())
    authority_inventory = b"".join(f"{sha(path)}  {path}\n".encode() for path in files)
    assert len(files) == e["authorities"]["active_authority_file_count"]
    assert hashlib.sha256(authority_inventory).hexdigest() == e["authorities"]["active_authority_inventory_sha256"]
    assert not [p for p in files if "trainer" in p.name.lower()]
    assert not [p for p in files if b"trainer" in p.read_bytes().lower()]
    assert tree_digest(OTHER) == (e["authorities"]["other_profile"]["tree_sha256"], e["authorities"]["other_profile"]["entry_count"])

    show = subprocess.run(
        ["systemctl", "--user", "show", "hermes-gateway-dualcoachtest.service", "-p", "ActiveState", "-p", "SubState", "-p", "MainPID"],
        check=True, capture_output=True, text=True,
    ).stdout.splitlines()
    values = dict(line.split("=", 1) for line in show)
    assert values == {"MainPID": "591358", "ActiveState": "active", "SubState": "running"}
    state = load(PROFILE / "gateway_state.json")
    assert state["pid"] == 591358 and state["gateway_state"] == "running"
    assert state["platforms"]["telegram"]["state"] == "connected"
    return {"checks": 54, "schema": "task26-live-lifecycle-independent-verification-v1", "status": "PASS", "mutations": 0}


def verify_inventory() -> dict[str, Any]:
    inventory = load(ROOT / "inventory.json")
    assert inventory["schema"] == "task26-live-lifecycle-inventory-v1"
    for row in inventory["entries"]:
        path = ROOT / row["path"]
        assert path.stat().st_size == row["bytes"] and sha(path) == row["sha256"]
    payload = json.dumps(inventory, sort_keys=True, separators=(",", ":")).encode()
    seal = load(ROOT / "SEAL.json")
    assert hashlib.sha256(payload).hexdigest() == seal["inventory_sha256"]
    assert seal["sealed_file_count"] == len(inventory["entries"])
    binding = seal["root_binding"]
    assert hashlib.sha256(json.dumps(binding, sort_keys=True, separators=(",", ":")).encode()).hexdigest() == seal["root_sha256"]
    by_path = {row["path"]: row["sha256"] for row in inventory["entries"]}
    assert binding["candidate"] == "4a6c7ee54cf9526a30de8bb576c1d71b411938beba33a04914738f6e1b6ed1cb"
    assert binding["evidence_sha256"] == by_path["lifecycle-evidence.json"]
    assert binding["independent_verification_sha256"] == by_path["independent-verification.json"]
    return {"checks": len(inventory["entries"]) + 7, "schema": "task26-live-lifecycle-inventory-verification-v1", "status": "PASS", "mutations": 0}


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--inventory-only", action="store_true")
    args = parser.parse_args()
    result = verify_inventory() if args.inventory_only else verify_live(load(ROOT / "lifecycle-evidence.json"))
    print(json.dumps(result, sort_keys=True, separators=(",", ":")))
    return 0


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