#!/usr/bin/env python3
import datetime
import hashlib
import json
import os
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
GENRES = ["Ngôn tình đô thị", "Cưới trước yêu sau", "Gia đình đời thường", "Tình cảm trưởng thành"]


def sha256(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 atomic_bytes(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    part = path.with_suffix(path.suffix + ".part")
    with open(part, "xb") as handle:
        handle.write(data)
        handle.flush()
        os.fsync(handle.fileno())
    os.replace(part, path)


def atomic_json(path, value):
    atomic_bytes(path, (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8"))


def main():
    manifest_path = ROOT / "script/project-manifest.json"
    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    if manifest.get("active_run") != "run-002" or manifest.get("steps", {}).get("story") != "completed":
        raise RuntimeError("metadata blocked until active run-002 Story Promotion Gate PASS")
    active = manifest["active_paths"]
    brief_path = ROOT / active["story_brief"]
    promotion_path = ROOT / active["promotion_receipt"]
    transcode_path = ROOT / active["transcode_receipt"]
    upload = ROOT / active["final_upload"]
    output = ROOT / active["metadata"]
    receipt_path = ROOT / active["metadata_receipt"]
    if output.exists() or receipt_path.exists():
        raise RuntimeError("metadata output/receipt must be virgin")

    brief = json.loads(brief_path.read_text(encoding="utf-8"))
    promotion = json.loads(promotion_path.read_text(encoding="utf-8"))
    transcode = json.loads(transcode_path.read_text(encoding="utf-8"))
    title = brief.get("canonical_title")
    if title != "Chúng Ta Không Cưới Để Thay Ca" or manifest.get("story_title") != title:
        raise RuntimeError("canonical title drift")
    if brief.get("story_family_primary") != "Cưới trước yêu sau" or brief.get("authority_revision") != "v2":
        raise RuntimeError("Brief family/revision drift")
    canon = manifest.get("canon_sha256")
    if promotion.get("verified") is not True or promotion.get("source_canon_sha256") != canon:
        raise RuntimeError("promotion lineage failed")
    if transcode.get("verified") is not True or transcode.get("source_canon_sha256") != canon:
        raise RuntimeError("transcode lineage failed")
    if not upload.is_file() or upload.stat().st_size >= 1_000_000_000 or sha256(upload) != transcode.get("artifact_sha256"):
        raise RuntimeError("upload copy lineage/size failed")

    youtube_title = f"Truyện Audio | {title} (Full) | Gác Mái Audio"
    summary = (
        "Sau tám tháng kết hôn, Tống Dao Quỳnh và Vệ Hoài Túc vẫn sống bằng hai ca lệch, chăm nhau qua những việc nhỏ "
        "nhưng luôn để hai gia đình xem cuộc hôn nhân của họ như một nguồn người thay ca. Khi khoản bảo lãnh Dao Quỳnh "
        "tự ký khiến quỹ khẩn cấp biến mất và căn hộ không thể gia hạn, họ chỉ còn mười tám ngày để tìm nhà. Hai lối thoát "
        "rẻ nhất lần lượt xuất hiện, nhưng mỗi lối đều buộc một người đem thời gian và công việc của người kia đi trả nợ."
    )
    description = (
        f"Bạn đang lắng nghe {title} tại Gác Mái Audio.\n\n"
        f"Tóm tắt truyện:\n{summary}\n\n"
        f"🎧 Tên truyện: {title}\n"
        "🎙 Giọng đọc: Gác Mái Audio\n"
        f"📚 Thể loại: {', '.join(GENRES)}\n"
        "Tình trạng: Trọn bộ\n\n"
        "Nếu yêu thích câu chuyện, hãy đăng ký kênh và để lại cảm nhận của bạn dưới phần bình luận.\n\n"
        "Gác Mái Audio – Lắng nghe một thế giới khác.\n\n"
        "Nội dung được phát hành độc quyền tại Gác Mái Audio. Vui lòng không sao chép và đăng tải lại dưới bất kỳ hình thức nào.\n\n"
        "#GacMaiAudio #TruyenAudio #NgheTruyen #TruyenHay #TruyenDemKhuya #TruyenNgonTinh #CuoiTruocYeuSau\n"
    )
    full = (youtube_title + "\n\n" + description).encode("utf-8")
    if not 2 <= len(youtube_title) <= 100:
        raise RuntimeError("YouTube title length invalid")
    rendered = full.decode("utf-8")
    if rendered.splitlines()[0] != youtube_title or rendered.count(title) < 3:
        raise RuntimeError("metadata title contract failed")

    atomic_bytes(output, full)
    now = datetime.datetime.now(datetime.timezone.utc).isoformat()
    receipt = {
        "status": "completed",
        "verified": True,
        "source_canon_sha256": canon,
        "canonical_title": title,
        "youtube_title": youtube_title,
        "youtube_title_length": len(youtube_title),
        "description": description,
        "genres": GENRES,
        "series_status": "Trọn bộ",
        "spoiler_safe": True,
        "exact_title_verified": True,
        "sample_literal_leakage": False,
        "source_authority": {
            "story_brief": active["story_brief"],
            "story_brief_sha256": sha256(brief_path),
            "promotion_receipt": active["promotion_receipt"],
            "promotion_receipt_sha256": sha256(promotion_path),
            "transcode_receipt": active["transcode_receipt"],
            "transcode_receipt_sha256": sha256(transcode_path),
        },
        "output_path": active["metadata"],
        "artifact_sha256": sha256(output),
        "artifact_bytes": output.stat().st_size,
        "completed_at": now,
    }
    atomic_json(receipt_path, receipt)
    manifest["steps"]["metadata"] = "completed"
    manifest["metadata"] = {
        "status": "completed",
        "verified": True,
        "receipt": active["metadata_receipt"],
        "artifact_sha256": receipt["artifact_sha256"],
        "completed_at": now,
    }
    manifest["updated_at"] = now
    atomic_json(manifest_path, manifest)
    print(json.dumps({"status": "completed", "verified": True, "title": youtube_title, "sha256": receipt["artifact_sha256"]}, ensure_ascii=False))


if __name__ == "__main__":
    main()
