#!/usr/bin/env python3
from __future__ import annotations

import hashlib
import json
import os
import re
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
MANIFEST_PATH = ROOT / "script/project-manifest.json"


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(8 * 1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def resolve_active(manifest: dict, key: str) -> Path:
    value = (manifest.get("active_paths") or {}).get(key)
    if not isinstance(value, str) or not value:
        raise RuntimeError(f"active_paths.{key} is missing")
    path = Path(value)
    if not path.is_absolute():
        path = ROOT / path
    try:
        path.resolve().relative_to(ROOT.resolve())
    except ValueError as exc:
        raise RuntimeError(f"active path escapes project: {key}") from exc
    return path


def atomic_json(path: Path, value: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    data = (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
    fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
    try:
        with os.fdopen(fd, "wb") as handle:
            handle.write(data)
            handle.flush()
            os.fsync(handle.fileno())
        os.replace(name, path)
    finally:
        if os.path.exists(name):
            os.unlink(name)


def main() -> int:
    project = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
    files = {
        "candidate": resolve_active(project, "candidate"),
        "canon": resolve_active(project, "story_canon"),
        "narration": resolve_active(project, "spoken_narration"),
        "promotion": resolve_active(project, "promotion_receipt"),
        "producer_close": resolve_active(project, "producer_close_receipt"),
        "story_manifest": resolve_active(project, "story_manifest"),
        "output": resolve_active(project, "promotion_verification_receipt"),
    }
    missing = [str(path) for key, path in files.items() if key != "output" and not path.exists()]
    if missing:
        raise RuntimeError(f"promotion bundle missing: {missing}")
    promotion = json.loads(files["promotion"].read_text(encoding="utf-8"))
    story = json.loads(files["story_manifest"].read_text(encoding="utf-8"))
    close = json.loads(files["producer_close"].read_text(encoding="utf-8"))
    candidate_bytes = files["candidate"].read_bytes()
    canon_bytes = files["canon"].read_bytes()
    narration_bytes = files["narration"].read_bytes()
    candidate_text = candidate_bytes.decode("utf-8", errors="strict")
    hashes = {name: sha256(files[name]) for name in ("candidate", "canon", "narration")}
    hygiene = {
        "heading": bool(re.search(r"(?m)^\s*#{1,6}\s+", candidate_text)),
        "scene_marker": bool(re.search(r"(?mi)^\s*(?:chương|phần|cảnh|scene|act)(?:\s+\d+|\s*[:.-])", candidate_text)),
        "placeholder": bool(re.search(r"\b(?:TODO|TBD|PLACEHOLDER)\b", candidate_text, re.I)),
        "channel_cta": bool(re.search(r"Gác Mái Audio|đăng ký kênh|like video", candidate_text, re.I)),
    }
    gate_receipts = promotion.get("gate_receipts", {})
    gate_receipts_valid = True
    gate_details = {}
    for name, value in gate_receipts.items():
        path_value = value.get("path") if isinstance(value, dict) else None
        expected = value.get("sha256") if isinstance(value, dict) else None
        path = Path(path_value) if isinstance(path_value, str) else Path("/__missing__")
        valid = path.exists() and expected == sha256(path)
        gate_receipts_valid = gate_receipts_valid and valid
        gate_details[name] = {"path": path_value, "expected_sha256": expected, "observed_sha256": sha256(path) if path.exists() else None, "verified": valid}
    checks = {
        "promotion_verified": promotion.get("status") == "completed" and promotion.get("verified") is True,
        "producer_closed": close.get("status") == "completed" and close.get("writer_closed") is True and close.get("candidate_sha256") == hashes["candidate"],
        "byte_equality": candidate_bytes == canon_bytes == narration_bytes,
        "hash_equality": len(set(hashes.values())) == 1,
        "promotion_hashes": promotion.get("candidate_sha256") == hashes["candidate"] and promotion.get("canon_sha256") == hashes["canon"] and promotion.get("spoken_narration_sha256") == hashes["narration"],
        "story_manifest_hashes": story.get("candidate_sha256") == hashes["candidate"] and story.get("canon_sha256") == hashes["canon"] and story.get("spoken_narration_sha256") == hashes["narration"],
        "project_manifest_hashes": project.get("canon_sha256") == hashes["canon"] and project.get("spoken_narration_sha256") == hashes["narration"],
        "manifest_states": story.get("status") == "promoted" and story.get("media_gate") == "open" and project.get("status") == "story_promoted" and project.get("steps", {}).get("story") == "completed" and project.get("steps", {}).get("voice_test") == "completed",
        "word_count": promotion.get("words_whitespace") == len(candidate_text.split()),
        "narration_hygiene": not any(hygiene.values()),
        "gate_receipts_hashed": len(gate_details) == 5 and gate_receipts_valid,
    }
    verified = all(checks.values())
    result = {
        "status": "completed" if verified else "failed", "verified": verified,
        "candidate_sha256": hashes["candidate"], "canon_sha256": hashes["canon"],
        "spoken_narration_sha256": hashes["narration"], "words_whitespace": len(candidate_text.split()),
        "checks": {name: {"verified": value, "value": value} for name, value in checks.items()},
        "gate_receipts": gate_details, "hygiene_findings": hygiene,
        "blockers": [name for name, value in checks.items() if not value],
    }
    atomic_json(files["output"], result)
    print(json.dumps({"status": result["status"], "verified": verified, "blockers": result["blockers"], "receipt": str(files["output"])}, ensure_ascii=False))
    return 0 if verified else 1


if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as exc:
        print(json.dumps({"status": "failed", "verified": False, "error": f"{type(exc).__name__}: {exc}"}, ensure_ascii=False), file=sys.stderr)
        sys.exit(1)
