"""Create concept QA checklists and central review tracking manifests."""

from __future__ import annotations

import csv
import hashlib
import json
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1] / "content" / "speech"
PLAN = json.loads((ROOT / "plan" / "content-plan.json").read_text(encoding="utf-8"))
DOMAINS = {
"clinical":["definition matches intended concept","inclusions and exclusions are clinically usable","targets are unambiguous","distractors follow staged difficulty","prompts preserve communicative intent","scoring separates correctness and independence","generalisation evidence is adequate","content is appropriate for individualization"],
"cultural":["examples avoid stereotypes","people and families are represented respectfully","foods objects routines and settings allow local alternatives","gesture and social meaning are culturally appropriate","wording is natural for the approved variety","images and video do not imply one culture is normative","caregiver terminology follows family preference","review decisions and regional limits are recorded"],
"accessibility":["meaning works without audio","meaning is not encoded by colour alone","targets remain visible at small playback size","captions reflect final meaningful audio","audio description covers essential visual meaning","motion avoids flash rapid cuts and vestibular triggers","touch switch keyboard eye-gaze and AAC paths are supported","stop pause replay and reduced-motion controls work"],
"licensing":["creator or source is recorded","generation provider is recorded where applicable","model and property release need is assessed","no brand logo watermark or unlicensed character appears","source and derivative rights permit intended distribution","release identifiers are stored outside public content","licence restrictions and expiry are recorded","checksum links the review to the exact immutable asset"],
"technical":["file exists at manifest path","filename and stable key match","format dimensions duration and encoding meet specification","checksum matches reviewed file","JSON and CSV parse successfully","all referenced concept node locale and asset keys resolve","video and audio continuity and loudness checks pass","artifact loads in the target Unity build without fallback error"],
}
ARTIFACTS=["semantic_definition","concept_images","video_generation_plan","video_storyboards","video_production_script","unity_game_config","unity_game_sample","english_learning_content","translation_packages","recording_packs","rendered_videos","recorded_audio"]

def concept_path(c): return ROOT/"concepts"/Path(*c["category"].split("."))/c["key"]
def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest()
def rel(path): return str(path.relative_to(ROOT)).replace("\\","/")

queues={d:[["review_key","concept_key","artifact_group","artifact_count","manifest_path","review_status","reviewer","evidence_reference","reviewed_checksum_or_manifest_hash","decision","reviewed_at","notes"]] for d in DOMAINS}
updated=0
for c in PLAN["concepts"]:
    base=concept_path(c); key=c["key"]
    groups={
      "semantic_definition":[base/"semantic-definition.json"],
      "concept_images":sorted((base/"images").glob("*.png"))+[base/"images/assets.csv",base/"images/generation-record.json"],
      "video_generation_plan":[base/"videos/video-generation-plan.json"],
      "video_storyboards":sorted((base/"videos").glob("*_setup_01.png"))+[base/"videos/storyboard-setups.json"],
      "video_production_script":[base/"videos/video-production-script.json"],
      "unity_game_config":[base/"games/unity-game-config.json",base/"games/games-list.csv"],
      "unity_game_sample":sorted((base/"games").glob("*_unity_game_sample_01.png")),
      "english_learning_content":[base/"locales/en/learning-content.json"],
      "translation_packages":sorted((base/"locales").glob("*/translation-package.json")),
      "recording_packs":sorted((base/"locales").glob("*/audio/recording-script.json"))+sorted((base/"locales").glob("*/audio/pronunciation-sheet.json"))+sorted((base/"locales").glob("*/audio/voice-actor-directions.txt")),
      "rendered_videos":sorted((base/"videos").glob("*.mp4")),
      "recorded_audio":[p for p in (base/"locales").glob("*/audio/*.wav")],
    }
    artifacts=[]
    for name in ARTIFACTS:
        files=groups[name]; missing_expected = (name=="unity_game_sample" and len(files)<1) or (name=="rendered_videos" and len(files)<3) or (name=="recorded_audio" and len(files)<1)
        present=[p for p in files if p.exists()]
        status="production_incomplete" if missing_expected or len(present)!=len(files) else "ready_for_review"
        inventory=[{"path":rel(p),"sha256":sha(p),"bytes":p.stat().st_size} for p in present]
        manifest_hash=hashlib.sha256(json.dumps(inventory,sort_keys=True).encode()).hexdigest() if inventory else None
        artifacts.append({"artifact_group":name,"expected_minimum":3 if name=="rendered_videos" else (1 if name in {"unity_game_sample","recorded_audio"} else len(files)),"actual_count":len(present),"status":status,"files":inventory,"manifest_hash":manifest_hash})
        for domain in DOMAINS:
            queues[domain].append([f"{key}.{name}.{domain}",key,name,len(present),f"concepts/{'/'.join(c['category'].split('.'))}/{key}/qa/review-manifest.json","blocked_by_production" if status=="production_incomplete" else "pending_review","","",manifest_hash or "","","",""])
    manifest={"schema_version":1,"concept_key":key,"artifact_groups":artifacts,"review_domains":{d:{"status":"pending","reviewer":None,"evidence":[],"decision":None,"reviewed_at":None,"notes":[]} for d in DOMAINS},"release_status":"not_approved"}
    checklist={"schema_version":1,"concept_key":key,"instructions":"Review the exact manifest hashes. A pass in one domain does not imply approval in another domain.","domains":{d:[{"check_id":f"{d}.{i+1:02d}","requirement":text,"result":None,"evidence_reference":None,"reviewer_note":None} for i,text in enumerate(items)] for d,items in DOMAINS.items()},"final_gate":{"all_domains_pass":False,"blocking_issues":[],"release_decision":"not_approved","approved_version":None}}
    for name,data in [("review-manifest.json",manifest),("review-checklist.json",checklist)]:
        p=base/"qa"/name; text=json.dumps(data,ensure_ascii=False,indent=2)+"\n"
        if not p.exists() or p.read_text(encoding="utf-8")!=text: p.write_text(text,encoding="utf-8"); updated+=1

tracking=ROOT/"tracking"/"reviews"; tracking.mkdir(parents=True,exist_ok=True)
for domain,rows in queues.items():
    with (tracking/f"{domain}-review.csv").open("w",encoding="utf-8",newline="") as stream: csv.writer(stream).writerows(rows)
summary={"schema_version":1,"concepts":len(PLAN["concepts"]),"domains":list(DOMAINS),"artifact_groups":ARTIFACTS,"rows_per_domain":len(PLAN["concepts"])*len(ARTIFACTS),"release_rule":"Every required artifact must be complete and every domain must pass against the same manifest hash.","status":"review_not_started"}
(tracking/"review-program.json").write_text(json.dumps(summary,ensure_ascii=False,indent=2)+"\n",encoding="utf-8")
print(f"QA manifests ready: {len(PLAN['concepts'])} concepts; {updated} concept files updated; {len(PLAN['concepts'])*len(ARTIFACTS)} rows per review domain")
