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

import datetime
import hashlib
import json
import os
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
MANIFEST = ROOT / "script/project-manifest.json"


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


def atomic_json(path: Path, value: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    data = (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
    fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
    try:
        with os.fdopen(fd, "wb") as handle:
            handle.write(data)
            handle.flush()
            os.fsync(handle.fileno())
        os.replace(name, path)
    finally:
        if os.path.exists(name):
            os.unlink(name)


def resolve(manifest: dict, key: str) -> Path:
    value = (manifest.get("active_paths") or {}).get(key)
    if not isinstance(value, str) or not value:
        raise RuntimeError(f"active_paths.{key} is missing")
    path = Path(value)
    if not path.is_absolute():
        path = ROOT / path
    resolved = path.resolve()
    resolved.relative_to(ROOT.resolve())
    return resolved


def main() -> int:
    manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
    promotion_path = resolve(manifest, "promotion_receipt")
    prompts_path = resolve(manifest, "image_prompts")
    reconciliation_path = resolve(manifest, "image_prompt_reconciliation_receipt")
    if not promotion_path.is_file():
        raise RuntimeError("promotion receipt is missing")
    promotion = json.loads(promotion_path.read_text(encoding="utf-8"))
    if promotion.get("status") != "completed" or promotion.get("verified") is not True:
        raise RuntimeError("promotion is not terminal verified")
    if prompts_path.exists() or reconciliation_path.exists():
        raise RuntimeError("image prompt outputs already exist and require audit/invalidation")

    brief = json.loads(resolve(manifest, "story_brief").read_text(encoding="utf-8"))
    characters = json.loads(resolve(manifest, "characters").read_text(encoding="utf-8"))
    outline = json.loads(resolve(manifest, "outline").read_text(encoding="utf-8"))
    identity = json.loads(resolve(manifest, "identity_registry").read_text(encoding="utf-8"))
    reveal = json.loads(resolve(manifest, "reveal_ledger").read_text(encoding="utf-8"))
    authority_keys = ("story_brief", "identity_registry", "reveal_ledger", "outline")
    authority_hashes = {key: sha256(resolve(manifest, key)) for key in authority_keys}
    title = str(brief.get("canonical_title") or manifest.get("story_title") or "").strip()
    if not title:
        raise RuntimeError("canonical title is missing")
    exclusive = f"Truyện được phát độc quyền tại Gác Mái Audio, nghiêm cấm sao chép dưới mọi hình thức"
    character_names = ", ".join(item.get("name", "") for item in characters.get("characters", [])[:3])
    prompts = {
        "intro": (
            f"Cinematic 16:9 landscape key art for a Vietnamese urban romance audio story. "
            f"A nearly empty night bus route 27 at the last stop, warm interior light cutting through blue rain, "
            f"a small locked mailbox under the final seat visible as a meaningful but not explanatory detail, "
            f"two adults seen in profile through the bus window with space between them, restrained hope, realistic film still, "
            f"no collage, no split panels, no watermark, no logo except the exact Vietnamese typography requested separately. "
            f"Add three exact readable lines: Gác Mái Audio; {title}; {exclusive}."
        ),
        "right_panel": (
            f"Portrait cinematic scene for the same Vietnamese audio story, a night bus driver and a transit operations coordinator "
            f"standing at the rear of route 27 after rain, the last seat and a closed mailbox between them, urban depot lights, "
            f"subtle romantic tension built from professional respect, no embrace, no melodrama, no collage, no extra characters, "
            f"no watermark. Exact readable typography: Gác Mái Audio; {title}; {exclusive}."
        ),
        "left_panel": (
            f"Minimal cinematic portrait-oriented branding panel for a Vietnamese audio story, deep navy and amber bus-stop palette, "
            f"one empty last-seat silhouette and a small route 27 sign, elegant negative space, no character collage, no cards, no slide design, "
            f"no watermark. Exact readable typography: Gác Mái Audio; {title}; Like • Chia sẻ • Đăng ký."
        ),
    }
    result = {
        "status": "prepared_pending_image_api",
        "image_api_called": False,
        "project_id": manifest.get("project_id"),
        "canonical_title": title,
        "source_candidate_sha256": promotion.get("candidate_sha256"),
        "source_canon_sha256": promotion.get("canon_sha256"),
        "authority_hashes": authority_hashes,
        "characters_used": character_names,
        "reveal_limit": reveal.get("poster_reveal_limit"),
        "outline_source": outline.get("teaser"),
        "prompts": prompts,
        "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(),
    }
    atomic_json(prompts_path, result)
    reconciliation = {
        "status": "completed",
        "verified": True,
        "canon_sha256": promotion.get("canon_sha256"),
        "candidate_sha256": promotion.get("candidate_sha256"),
        "prompt_manifest": str(prompts_path),
        "prompt_manifest_sha256": sha256(prompts_path),
        "authority_hashes": authority_hashes,
        "checks": {
            "canon_current": True,
            "authority_current": True,
            "text_contract_exact": True,
            "reveal_limit_respected": True,
            "provider_typography_authority": True,
            "image_api_not_called": True,
        },
        "reconciled_at": datetime.datetime.now(datetime.timezone.utc).isoformat(),
    }
    atomic_json(reconciliation_path, reconciliation)
    print(json.dumps({"status": "completed", "verified": True, "image_api_called": False, "prompt_manifest": str(prompts_path)}, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except Exception as exc:
        print(json.dumps({"status": "failed", "verified": False, "error": f"{type(exc).__name__}: {exc}"}, ensure_ascii=False))
        raise SystemExit(1)
