#!/usr/bin/env python3
import hashlib
import json
import re
import sys
from pathlib import Path

PROJECT = Path(__file__).resolve().parents[1]
CHAPTERS = PROJECT / "story/chapters"
SPOKEN = PROJECT / "story/spoken-narration.txt"
CANON = PROJECT / "story/story-canon.txt"
REPORT = PROJECT / "story/draft-report.json"
BRIEF = PROJECT / "script/story-brief.json"
OUTLINE = PROJECT / "script/outline.json"
LEDGER = PROJECT / "script/story-ledger.json"
IDENTITY = PROJECT / "script/identity-registry.json"
REVEAL = PROJECT / "script/reveal-ledger.json"
PLAN = PROJECT / "script/production-plan.json"
BASELINE = PROJECT / "log/story-authority-baseline.json"
WORD_RE = re.compile(r"[^\W_]+(?:['’-][^\W_]+)*", re.UNICODE)


def sha(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def report_value(report, *names):
    for name in names:
        if name in report:
            return report[name]
    aggregates = report.get("aggregates", {})
    for name in names:
        if name in aggregates:
            return aggregates[name]
    return None


def check_report_gate(report, key):
    checks = report.get("checks", {})
    gates = report.get("gates", {})
    value = checks.get(key, gates.get(key))
    if isinstance(value, dict):
        return value.get("verified") is True or value.get("passed") is True
    return value is True


def main():
    authority = [BRIEF, OUTLINE, LEDGER, IDENTITY, REVEAL, PLAN]
    chapters = [CHAPTERS / f"chapter-{index:02d}.txt" for index in range(1, 11)]
    required = authority + [BASELINE, REPORT, SPOKEN, CANON] + chapters
    missing = [str(path) for path in required if not path.is_file() or path.stat().st_size == 0]
    if missing:
        raise RuntimeError("Story gate missing artifacts: " + ", ".join(missing))
    brief = json.loads(BRIEF.read_text(encoding="utf-8"))
    outline = json.loads(OUTLINE.read_text(encoding="utf-8"))
    report = json.loads(REPORT.read_text(encoding="utf-8"))
    baseline = json.loads(BASELINE.read_text(encoding="utf-8"))
    joined = "\n\n\n".join(path.read_text(encoding="utf-8").rstrip() for path in chapters) + "\n"
    spoken = SPOKEN.read_text(encoding="utf-8")
    canon = CANON.read_text(encoding="utf-8")
    words = len(WORD_RE.findall(spoken))
    low = int(brief["target_words"]["min"])
    high = int(brief["target_words"]["max"])
    authority_hashes = {path.name: sha(path) for path in authority}
    baseline_files = baseline.get("files", {})
    baseline_match = baseline.get("verified") is True and all(
        baseline_files.get(str(path.relative_to(PROJECT)), {}).get("sha256") == sha(path)
        for path in authority
    )
    report_hashes = report.get("authority_hashes", {})
    chapter_rows = report.get("chapter_rows", report.get("chapters_detail", []))
    chapter_count = report.get("chapters", report.get("chapters_present", len(chapter_rows)))
    report_words = report_value(report, "word_count", "spoken_word_count", "aggregate_word_count")
    report_spoken = report_value(report, "spoken_sha256", "narration_sha256")
    report_canon = report_value(report, "canon_sha256")
    family_ok = (
        brief.get("primary_story_family") == "Thiên kim thật–giả / gia đình phản diện / trở về"
        and brief.get("secondary_story_family") == "Cưới trước yêu sau"
        and outline.get("primary_story_family") == brief.get("primary_story_family")
        and outline.get("secondary_story_family") == brief.get("secondary_story_family")
    )
    required_report_gates = (
        "global_coherence", "comprehension", "dialogue", "audio", "originality",
        "reveal", "primary_family_adherence", "secondary_family_adherence",
        "heading", "production_marker", "whitespace", "duplicate_paragraph",
        "chapter_count", "word_range", "exact_join",
    )
    report_gates = {key: check_report_gate(report, key) for key in required_report_gates}
    if report_hashes:
        authority_match = all(report_hashes.get(name) == digest for name, digest in authority_hashes.items())
    else:
        authority_match = all(report.get(name.replace(".json", "") + "_sha256") == digest for name, digest in authority_hashes.items() if name in {"outline.json", "identity-registry.json", "reveal-ledger.json"})
    checks = {
        "exact_join": spoken == joined,
        "canon_byte_identical": canon == spoken,
        "word_range": low <= words <= high,
        "duration_range": 40 <= words / 231 <= 60,
        "brief_locked": brief.get("verified") is True and brief.get("state") == "VOICE_LOCK",
        "architecture_locked": outline.get("verified") is True and outline.get("architecture_locked") is True,
        "authority_baseline_unchanged": baseline_match,
        "family_authority_locked": family_ok,
        "report_verified": report.get("verified") is True and not report.get("issues"),
        "chapter_count": chapter_count == 10 and len(chapters) == 10,
        "report_word_count": report_words == words,
        "report_spoken_hash": report_spoken == sha(SPOKEN),
        "report_canon_hash": report_canon == sha(CANON),
        "report_authority_hashes": authority_match,
        "report_promotion_unperformed": report.get("promotion_performed") is False,
        "report_gates": all(report_gates.values()),
    }
    verified = all(checks.values())
    result = {
        "verified": verified,
        "word_count": words,
        "accepted_range": [low, high],
        "duration_minutes_at_231_wpm": round(words / 231, 3),
        "hashes": {"spoken": sha(SPOKEN), "canon": sha(CANON), **authority_hashes},
        "checks": checks,
        "report_gates": report_gates,
    }
    print(json.dumps(result, ensure_ascii=False))
    return 0 if verified else 1


if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as exc:
        print(f"Story gate blocked: {exc}", file=sys.stderr)
        sys.exit(1)
