#!/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"
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("source_canon_sha256") != canon:
        print("Intro blocked: authority receipt mismatch", file=sys.stderr)
        return 1

    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)
    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)), "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())
