#!/usr/bin/env python3
from __future__ import annotations

import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path("/data/video-pipeline/HaTramAudio/project/014-Nguoi-Ve-Ban-Do-Mua-Dong")
PROJECT_ID = "014-Nguoi-Ve-Ban-Do-Mua-Dong"
RUN_ID = "run-20260720T152357Z-c85a2128"
OWNER = "zoro"
OUTPUT = ROOT / "log" / "publish-ready.json"


def now() -> str:
    return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def sha(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for block in iter(lambda: f.read(8 * 1024 * 1024), b""):
            h.update(block)
    return h.hexdigest()


def guard() -> None:
    d = json.loads((ROOT / ".ownership-lock.json").read_text(encoding="utf-8"))
    for key, value in {"project_id": PROJECT_ID, "run_id": RUN_ID, "owner": OWNER, "status": "main_session_pipeline"}.items():
        if d.get(key) != value:
            raise RuntimeError(f"ownership mismatch: {key}")


def load(rel: str, statuses: tuple[str, ...]) -> dict:
    path = ROOT / rel
    data = json.loads(path.read_text(encoding="utf-8"))
    if data.get("project_id", PROJECT_ID) != PROJECT_ID or data.get("run_id") != RUN_ID:
        raise RuntimeError(f"identity mismatch: {rel}")
    if data.get("verified") is not True or data.get("status") not in statuses:
        raise RuntimeError(f"not terminal verified: {rel}")
    return data


def match(path: Path, expected: str, label: str) -> dict:
    if not path.is_file():
        raise RuntimeError(f"missing artifact: {label}")
    actual = sha(path)
    if actual != expected:
        raise RuntimeError(f"artifact hash mismatch: {label}")
    return {"path": str(path), "sha256": actual, "bytes": path.stat().st_size}


def main() -> None:
    guard()
    promotion = load("script/promotion-report.json", ("passed", "completed", "completed_content_only"))
    tts = load("script/tts-manifest.json", ("completed",))
    layout = load("image/layout-manifest.json", ("completed",))
    intro = load("log/intro-render.json", ("completed",))
    footage = load("log/footage-render.json", ("completed",))
    final = load("log/final-render.json", ("completed",))
    transcode = load("log/transcode-upload.json", ("completed",))
    metadata = load("log/metadata.json", ("completed",))
    canon_hash = promotion.get("canon_sha256") or promotion.get("source_canon_sha256")
    receipts = {"tts": tts, "layout": layout, "intro": intro, "footage": footage, "final": final, "transcode": transcode, "metadata": metadata}
    for name, receipt in receipts.items():
        if receipt.get("source_canon_sha256") != canon_hash:
            raise RuntimeError(f"canon binding mismatch: {name}")
    if not footage.get("visual_qa", {}).get("verified"):
        raise RuntimeError("footage visual QA missing")
    if not final.get("visual_qa", {}).get("verified"):
        raise RuntimeError("final visual QA missing")
    artifacts = {
        "canon": match(ROOT / "story" / "story-canon.txt", canon_hash, "canon"),
        "narration": match(ROOT / "story" / "spoken-narration.txt", promotion["spoken_narration_sha256"], "narration"),
        "tts": match(ROOT / "audio" / "story-full.wav", tts["output_sha256"], "tts"),
        "layout": match(ROOT / "image" / "normalized" / "layout-1920x1080.png", layout["layout_sha256"], "layout"),
        "intro": match(ROOT / "output" / "intro" / "intro.mp4", intro["artifact_sha256"], "intro"),
        "footage": match(ROOT / "output" / "footage" / "footage.mp4", footage["artifact_sha256"], "footage"),
        "final": match(ROOT / "output" / "final.mp4", final["artifact_sha256"], "final"),
        "upload_copy": match(ROOT / "output" / "final-upload.mp4", transcode["artifact_sha256"], "upload_copy"),
        "metadata": match(ROOT / "output" / "info.txt", metadata["artifact_sha256"], "metadata"),
    }
    if artifacts["upload_copy"]["bytes"] >= 1_000_000_000:
        raise RuntimeError("upload copy is not under 1,000,000,000 bytes")
    if transcode.get("source_final_sha256") != artifacts["final"]["sha256"]:
        raise RuntimeError("transcode direct-upstream hash mismatch")
    if final.get("source_footage_sha256") != artifacts["footage"]["sha256"] or final.get("source_intro_sha256") != artifacts["intro"]["sha256"]:
        raise RuntimeError("final direct-upstream hash mismatch")
    if footage.get("source_audio_sha256") != artifacts["tts"]["sha256"] or footage.get("source_layout_sha256") != artifacts["layout"]["sha256"]:
        raise RuntimeError("footage direct-upstream hash mismatch")
    report = {
        "schema_version": 1,
        "project_id": PROJECT_ID,
        "run_id": RUN_ID,
        "status": "ready",
        "verified": True,
        "source_canon_sha256": canon_hash,
        "artifacts": artifacts,
        "visual_qa": {"footage": True, "final": True},
        "upload_copy_under_one_gb": True,
        "postiz_upload_allowed": True,
        "postiz_schedule_allowed_after_calendar_recheck": True,
        "audited_at": now(),
    }
    guard()
    OUTPUT.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"status": "ready", "verified": True, "upload_copy_bytes": artifacts["upload_copy"]["bytes"]}, ensure_ascii=False))


if __name__ == "__main__":
    main()
