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

ROOT = Path(__file__).resolve().parents[1]
OLD_NAMES = ["Hạ An Nhiên", "Lâm Cảnh Hoài", "Hạ Minh Châu", "Trần Tú Mai", "Tô Uyển Dung", "Hạ Đình Sơn", "Hạ Tuấn Thành"]
CENTRAL_NAMES = ["Tống Dao Quỳnh", "Vệ Hoài Túc"]
SUPPORT_NAMES = ["Tống Chí Duy", "Tống Đức Khang", "Vệ Quang Nghiêm", "Vệ Như Tịnh", "Kha Mẫn Chi", "Lạc Thế Bình"]
PRODUCTION_TERMS = re.compile(r"(?i)(?<!\w)(teaser|cold open|catch-up|beat|scene|POV|outline|candidate|canon|narration|prompt|receipt|metadata)(?!\w)")
POLICY_VOICE = re.compile(r"(?i)(quyền lựa chọn|quyền đồng ý|phạm vi đồng thuận|không nói thay|không quyết định thay|quyền rút lại|cơ chế giám sát)")


def line_hits(text, pattern):
    regex = re.compile(pattern, re.IGNORECASE | re.UNICODE) if isinstance(pattern, str) else pattern
    return [i for i, line in enumerate(text.splitlines(), 1) if regex.search(line)]


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("directory", type=Path)
    args = parser.parse_args()
    directory = args.directory if args.directory.is_absolute() else ROOT / args.directory
    rows = []
    all_text = []
    hard_failures = []
    heuristics = []
    for number in range(1, 5):
        path = directory / f"part-{number:02d}.txt"
        if not path.exists():
            hard_failures.append({"part": number, "check": "missing"})
            continue
        raw = path.read_bytes()
        try:
            text = raw.decode("utf-8")
        except UnicodeDecodeError as exc:
            hard_failures.append({"part": number, "check": "utf8", "error": str(exc)})
            continue
        all_text.append(text)
        row = {
            "part": number,
            "path": str(path),
            "bytes": len(raw),
            "words": len(text.split()),
            "lines": len(text.splitlines()),
            "sha256": hashlib.sha256(raw).hexdigest(),
            "nfc": unicodedata.is_normalized("NFC", text),
            "one_final_lf": raw.endswith(b"\n") and not raw.endswith(b"\n\n"),
            "cr_count": raw.count(b"\r"),
            "tab_count": text.count("\t"),
            "heading_lines": line_hits(text, r"^\s*(?:#{1,6}\s+|(?:chương|phần|cảnh|hồi)\s*(?:\d+|[IVXLC]+|[:\-]))"),
            "production_term_lines": line_hits(text, PRODUCTION_TERMS),
            "cta_lines": line_hits(text, r"(?:đăng ký kênh|subscribe|nhấn chuông|hãy like|hãy chia sẻ)"),
            "policy_voice_lines": line_hits(text, POLICY_VOICE),
        }
        rows.append(row)
        for key in ["nfc", "one_final_lf"]:
            if not row[key]:
                hard_failures.append({"part": number, "check": key})
        for key in ["cr_count", "tab_count", "heading_lines", "production_term_lines", "cta_lines"]:
            if row[key]:
                hard_failures.append({"part": number, "check": key, "value": row[key]})
        if row["policy_voice_lines"]:
            heuristics.append({"part": number, "check": "policy_voice_review", "lines": row["policy_voice_lines"]})

    aggregate = "\n".join(all_text)
    total_words = len(aggregate.split())
    if not 9800 <= total_words <= 10800:
        hard_failures.append({"check": "total_words", "value": total_words, "target": [9800, 10800]})
    stale_timeline = {
        "literal_D16": line_hits(aggregate, r"D-16"),
        "sixteen_days_to_move": line_hits(aggregate, r"mười sáu ngày(?: nữa| để| tới| trước ngày bàn giao)"),
        "old_beat2_eighteen_days_before": line_hits(aggregate, r"mười tám ngày trước(?: đó| buổi sáng| khi máy giặt)"),
    }
    if any(stale_timeline.values()):
        hard_failures.append({"check": "authority_v1_timeline_markers", "value": stale_timeline})
    old_name_hits = {name: len(re.findall(re.escape(name), aggregate, re.IGNORECASE)) for name in OLD_NAMES}
    old_name_hits = {name: count for name, count in old_name_hits.items() if count}
    if old_name_hits:
        hard_failures.append({"check": "run001_names", "value": old_name_hits})
    cast_occurrences = {name: len(re.findall(re.escape(name), aggregate, re.IGNORECASE)) for name in CENTRAL_NAMES + SUPPORT_NAMES}
    missing_cast = [name for name, count in cast_occurrences.items() if count == 0]
    if missing_cast:
        hard_failures.append({"check": "missing_cast", "value": missing_cast})

    result = {
        "status": "PASS" if not hard_failures else "REVIEW_REQUIRED",
        "verified": not hard_failures,
        "scope": "raw temp draft; does not authorize official source-part close",
        "authority_revision_required": "v2",
        "parts": rows,
        "total_words": total_words,
        "cast_occurrences": cast_occurrences,
        "stale_timeline_probe": stale_timeline,
        "hard_failures": hard_failures,
        "heuristics": heuristics,
    }
    print(json.dumps(result, ensure_ascii=False, indent=2))
    raise SystemExit(0 if result["verified"] else 1)


if __name__ == "__main__":
    main()
