#!/usr/bin/env python3
import hashlib
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

PROJECT = Path(__file__).resolve().parents[1]
REPORT = PROJECT / "log/production-verification.json"


def load(rel):
    path = PROJECT / rel
    if not path.is_file():
        return None
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return None


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


def probe(path):
    result = subprocess.run([
        "ffprobe", "-v", "error", "-show_entries", "format=duration,size",
        "-show_entries", "stream=index,codec_name,width,height,sample_rate,channels",
        "-of", "json", str(path),
    ], capture_output=True, text=True, check=True)
    return json.loads(result.stdout)


def main():
    manifest = load("script/project-manifest.json")
    promotion = load("story/promotion-report.json")
    if not manifest or not promotion or promotion.get("verified") is not True:
        raise RuntimeError("manifest/promotion missing or invalid")
    canon = promotion.get("spoken_sha256")
    issues = []
    specs = {
        "pronunciation": "log/tts-pronunciation.json",
        "tts": "log/tts-verification.json",
        "images": "log/image-generation.json",
        "layout": "image/layout-manifest.json",
        "intro": "log/intro-render.json",
        "footage": "log/footage-job.json",
        "final": "log/final-render.json",
        "metadata": "log/metadata-verification.json",
    }
    receipts = {}
    for name, rel in specs.items():
        data = load(rel)
        receipts[name] = {"path": rel, "present": data is not None}
        if not data:
            issues.append("missing_or_invalid_receipt:" + name)
            continue
        source = data.get("source_canon_sha256") or data.get("spoken_sha256")
        receipts[name].update({"verified": data.get("verified") is True, "source_canon_sha256": source})
        if data.get("verified") is not True:
            issues.append("receipt_not_verified:" + name)
        if source != canon:
            issues.append("receipt_canon_mismatch:" + name)
    layout = load("image/layout-manifest.json") or {}
    pronunciation = load("log/tts-pronunciation.json") or {}
    tts = load("log/tts-verification.json") or {}
    intro = load("log/intro-render.json") or {}
    footage = load("log/footage-job.json") or {}
    final = load("log/final-render.json") or {}
    pronunciation_path = PROJECT / "story/tts-pronunciation.txt"
    audio_path = PROJECT / "audio/story-full.wav"
    if (
        pronunciation.get("semantic_content_changed") is not False
        or pronunciation.get("reverse_verified") is not True
        or not pronunciation_path.is_file()
        or pronunciation.get("output_sha256") != (sha(pronunciation_path) if pronunciation_path.is_file() else None)
        or tts.get("pronunciation_verified") is not True
        or tts.get("source_sha256") != pronunciation.get("output_sha256")
        or tts.get("pronunciation_receipt_sha256") != sha(PROJECT / "log/tts-pronunciation.json")
        or not audio_path.is_file()
        or tts.get("output_sha256") != (sha(audio_path) if audio_path.is_file() else None)
    ):
        issues.append("pronunciation_tts_chain")
    if layout.get("visual_qa", {}).get("verified") is not True:
        issues.append("layout_artwork_visual_qa")
    if intro.get("visual_qa", {}).get("verified") is not True:
        issues.append("intro_artwork_visual_qa")
    if footage.get("visual_qa", {}).get("required") is not False:
        issues.append("footage_visual_qa_must_be_not_required")
    if final.get("visual_qa", {}).get("required") is not False:
        issues.append("final_visual_qa_must_be_not_required")
    footage_server = footage.get("server_verification", {})
    if footage_server.get("mirror_applied") is not True or footage_server.get("source_audio_discarded") is not True:
        issues.append("footage_technical_verification")
    final_path = PROJECT / "output/final.mp4"
    final_probe = None
    if not final_path.is_file():
        issues.append("final_missing")
    else:
        try:
            final_probe = probe(final_path)
            streams = final_probe.get("streams", [])
            videos = [row for row in streams if row.get("width")]
            audios = [row for row in streams if row.get("sample_rate")]
            if len(videos) != 1 or len(audios) != 1 or videos[0].get("codec_name") != "h264" or (videos[0].get("width"), videos[0].get("height")) != (1920, 1080) or audios[0].get("codec_name") != "aac":
                issues.append("final_codec_dimensions_streams")
            if final.get("output_sha256") != sha(final_path):
                issues.append("final_hash_mismatch")
        except Exception as exc:
            issues.append("final_probe:" + str(exc))
    if not (PROJECT / "output/info.txt").is_file():
        issues.append("metadata_file_missing")
    report = {
        "version": 1, "verified": not issues, "status": "passed" if not issues else "failed",
        "source_canon_sha256": canon, "receipts": receipts,
        "final": {"path": "output/final.mp4", "probe": final_probe},
        "issues": issues, "checked_at": datetime.now(timezone.utc).isoformat(),
    }
    REPORT.parent.mkdir(parents=True, exist_ok=True)
    REPORT.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"verified": report["verified"], "issues": issues}, ensure_ascii=False))
    return 0 if report["verified"] else 1


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