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

import requests

PROJECT = Path(__file__).resolve().parents[1]
PROMOTION = PROJECT / "story/promotion-report.json"
LAYOUT = PROJECT / "image/layout-manifest.json"
POSTER = PROJECT / "image/intro-poster-1920x1080.png"
VOICE = PROJECT / "audio/intro-voice.wav"
SILENCE = PROJECT / "audio/intro-silence-2s.wav"
FULL = PROJECT / "audio/intro-full.wav"
OUTPUT = PROJECT / "output/intro/intro.mp4"
RECEIPT = PROJECT / "log/intro-render.json"
VOICE_RECEIPT = PROJECT / "log/intro-voice.json"
ENDPOINT = "http://192.168.40.32:7861/voice/ngoc-huyen-clone"
TEXT = "Các bạn đang nghe truyện được phát từ Huyền An Audio, chúc các bạn có những giây phút nghe truyện vui vẻ. Hãy ủng hộ chúng tôi bằng cách like video và đăng ký kênh."


def run(args):
    subprocess.run(args, check=True)


def digest(path):
    value = hashlib.sha256()
    with path.open("rb") as handle:
        for block in iter(lambda: handle.read(8 * 1024 * 1024), b""):
            value.update(block)
    return value.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():
    if not PROMOTION.exists() or not LAYOUT.exists() or not POSTER.exists():
        print("Intro blocked: promoted story or poster missing", file=sys.stderr)
        return 1
    promotion = json.loads(PROMOTION.read_text(encoding="utf-8"))
    layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
    canon = promotion.get("spoken_sha256")
    if promotion.get("verified") is not True or layout.get("verified") is not True or layout.get("visual_qa", {}).get("verified") is not True or layout.get("source_canon_sha256") != canon:
        print("Intro blocked: authority receipt mismatch", file=sys.stderr)
        return 1
    if RECEIPT.exists():
        previous = json.loads(RECEIPT.read_text(encoding="utf-8"))
        if previous.get("verified") is True and previous.get("source_canon_sha256") == canon and previous.get("poster_sha256") == digest(POSTER) and OUTPUT.exists() and previous.get("output_sha256") == digest(OUTPUT):
            print(json.dumps({"verified": True, "resumed": True, "duration_seconds": previous["video_duration_seconds"], "output": str(OUTPUT)}, ensure_ascii=False))
            return 0
    text_sha = hashlib.sha256(TEXT.encode()).hexdigest()
    voice_reuse = False
    if VOICE_RECEIPT.exists():
        voice_state = json.loads(VOICE_RECEIPT.read_text(encoding="utf-8"))
        if voice_state.get("status") == "completed" and voice_state.get("source_canon_sha256") == canon and voice_state.get("text_sha256") == text_sha and VOICE.exists() and voice_state.get("wav_sha256") == digest(VOICE):
            voice_reuse = True
        elif voice_state.get("status") == "submitting":
            print("Intro blocked: prior intro TTS result is uncertain; do not submit twice", file=sys.stderr)
            return 1
    if not voice_reuse:
        VOICE_RECEIPT.parent.mkdir(parents=True, exist_ok=True)
        VOICE_RECEIPT.write_text(json.dumps({"version": 1, "status": "submitting", "source_canon_sha256": canon, "text_sha256": text_sha, "submitted_at": datetime.now(timezone.utc).isoformat()}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        response = requests.post(ENDPOINT, data={"text": TEXT, "style": "doc_truyen", "speed": "1.0", "denoise": "true"}, timeout=900)
        response.raise_for_status()
        if not response.content.startswith(b"RIFF"):
            raise RuntimeError("Intro TTS did not return WAV")
        VOICE.parent.mkdir(parents=True, exist_ok=True)
        VOICE.write_bytes(response.content)
        VOICE_RECEIPT.write_text(json.dumps({"version": 1, "status": "completed", "verified": True, "source_canon_sha256": canon, "text_sha256": text_sha, "wav_sha256": digest(VOICE), "completed_at": datetime.now(timezone.utc).isoformat()}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-f", "lavfi", "-i", "anullsrc=r=48000:cl=mono", "-t", "2", str(SILENCE)])
    concat = PROJECT / "script/intro-concat.txt"
    concat.write_text(f"file '{VOICE.resolve()}'\nfile '{SILENCE.resolve()}'\n", encoding="utf-8")
    run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-f", "concat", "-safe", "0", "-i", str(concat), "-c", "copy", str(FULL)])
    full_probe = probe(FULL)
    duration = float(full_probe["format"]["duration"])
    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-loop", "1", "-framerate", "30", "-i", str(POSTER), "-i", str(FULL), "-t", f"{duration:.6f}", "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-pix_fmt", "yuv420p", "-r", "30", "-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart", str(OUTPUT)])
    voice_probe = probe(VOICE)
    output_probe = probe(OUTPUT)
    voice_duration = float(voice_probe["format"]["duration"])
    output_duration = float(output_probe["format"]["duration"])
    streams = output_probe.get("streams", [])
    verified = (
        abs((voice_duration + 2) - duration) < 0.05
        and abs(output_duration - duration) <= 1 / 30 + 0.005
        and len(streams) == 2
        and any(item.get("codec_name") == "h264" for item in streams)
        and any(item.get("codec_name") == "aac" for item in streams)
    )
    receipt = {
        "version": 1, "verified": verified, "source_canon_sha256": canon,
        "intro_text": TEXT, "voice_duration_seconds": voice_duration,
        "silence_seconds": 2.0, "full_audio_duration_seconds": duration,
        "video_duration_seconds": output_duration,
        "duration_delta_seconds": round(output_duration - duration, 6),
        "poster": str(POSTER.relative_to(PROJECT)), "poster_sha256": digest(POSTER), "output": str(OUTPUT.relative_to(PROJECT)),
        "output_sha256": digest(OUTPUT),
        "probe": output_probe, "checked_at": datetime.now(timezone.utc).isoformat(),
    }
    RECEIPT.parent.mkdir(parents=True, exist_ok=True)
    RECEIPT.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"verified": verified, "duration_seconds": output_duration, "output": str(OUTPUT)}, ensure_ascii=False))
    return 0 if verified else 1


if __name__ == "__main__":
    sys.exit(main())
