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

EXPECTED_PROJECT = "gacmai_20260716_155848"
FORBIDDEN = {
    "medical_or_hospital": r"\b(bệnh viện|bác sĩ|phẫu thuật|y tế|ca trực)\b",
    "inheritance_or_will": r"\b(di chúc|thừa kế)\b",
    "charity_fund": r"\b(quỹ cứu trợ|quỹ từ thiện)\b",
    "livestream_accusation": r"\b(livestream|phát trực tiếp)\b",
    "maps_planning_flood": r"\b(bản đồ|quy hoạch|chống lũ|mô phỏng nước)\b",
    "architecture": r"\b(kiến trúc|bản quyền kiến trúc)\b",
    "textile": r"\b(nhuộm|dệt|chứng nhận vải)\b",
    "railway_voice": r"\b(ga tàu|đường sắt|dữ liệu giọng nói)\b",
    "research_institute": r"\b(viện nghiên cứu)\b",
    "board_game": r"\b(trò chơi bàn|board game|quân bài)\b",
}

def fail(msg):
    print(json.dumps({"status": "failed", "error": msg}, ensure_ascii=False))
    raise SystemExit(1)

def main(path):
    p = Path(path)
    d = json.loads(p.read_text())
    if d.get("project_id") != EXPECTED_PROJECT:
        fail("project identity mismatch")
    if not isinstance(d.get("title"), str) or not d["title"].strip():
        fail("missing title")
    cast = d.get("cast")
    if not isinstance(cast, list) or len(cast) != 6 or len(set(cast)) != 6:
        fail("cast must contain exactly six unique full names")
    if any(len(x.split()) < 2 for x in cast):
        fail("cast entries must be full names")
    evidence = d.get("evidence")
    if not isinstance(evidence, list) or [x.get("id") for x in evidence] != [f"E{i:02d}" for i in range(1, 10)]:
        fail("evidence must be E01-E09")
    required = {"provenance", "lawful_acquisition", "custody", "setup_part", "preservation_part", "payoff_part", "supported_claim", "cannot_support"}
    for item in evidence:
        if not required.issubset(item) or not all(item.get(k) not in (None, "") for k in required):
            fail(f"incomplete evidence {item.get('id')}")
        if not (1 <= item["setup_part"] <= item["preservation_part"] <= item["payoff_part"] <= 12):
            fail(f"bad evidence order {item['id']}")
    parts = d.get("parts")
    if not isinstance(parts, list) or [x.get("part") for x in parts] != list(range(1, 13)):
        fail("parts must be 1-12")
    if sum(int(x.get("target_words", 0)) for x in parts) not in range(14400, 15001):
        fail("target word total outside contract")
    if any(x.get("paragraphs") != 12 for x in parts):
        fail("each part must lock 12 paragraphs")
    if d.get("pov") != "limited_to_female_protagonist":
        fail("POV contract mismatch")
    if d.get("part_8_gate") != "emotional_and_procedural_bottom":
        fail("P8 gate mismatch")
    if d.get("parts_10_11_gate") != "multi_layer_counterattack_without_livestream":
        fail("P10-P11 gate mismatch")
    if d.get("part_12_gate") != "closed_ending_and_aftercare":
        fail("P12 gate mismatch")
    scopes = d.get("authority_scopes")
    if not isinstance(scopes, list) or len(scopes) < 5:
        fail("at least five authority scopes required")
    text = json.dumps({k: d.get(k) for k in ("title", "domain", "premise", "parts", "evidence")}, ensure_ascii=False).lower()
    hits = [name for name, pat in FORBIDDEN.items() if re.search(pat, text, re.I)]
    if hits:
        fail("forbidden domains: " + ",".join(hits))
    result = {"status": "passed", "sha256": hashlib.sha256(p.read_bytes()).hexdigest(), "title": d["title"], "cast": cast, "parts": 12, "paragraphs": 144, "target_words": sum(x["target_words"] for x in parts), "evidence_threads": 9, "authority_scopes": len(scopes)}
    print(json.dumps(result, ensure_ascii=False))

if __name__ == "__main__":
    if len(sys.argv) != 2:
        fail("usage: validator OUTLINE.json")
    main(sys.argv[1])
