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

PROJECT = Path(__file__).resolve().parents[1]
LEFT = PROJECT / "image/left-panel-master.png"
RIGHT = PROJECT / "image/right-panel-master.png"
IMAGE_QA = PROJECT / "log/image-visual-qa.json"
PROMOTION = PROJECT / "story/promotion-report.json"
OUTPUT = PROJECT / "image/layout-1920x1080.png"
RECEIPT = PROJECT / "image/layout-manifest.json"


def sha(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 probe(path):
    result = subprocess.run(
        ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=codec_name,width,height,pix_fmt", "-of", "json", str(path)],
        capture_output=True, text=True, check=True,
    )
    return json.loads(result.stdout)["streams"][0]


def atomic_json(path, value):
    temp = path.with_name(path.name + ".part")
    temp.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    os.replace(temp, path)


def main():
    for path in (LEFT, RIGHT, IMAGE_QA, PROMOTION):
        if not path.is_file():
            raise RuntimeError(f"missing local-layout prerequisite: {path}")
    qa = json.loads(IMAGE_QA.read_text(encoding="utf-8"))
    promotion = json.loads(PROMOTION.read_text(encoding="utf-8"))
    if qa.get("verified") is not True or qa.get("source_canon_sha256") != promotion.get("canon_sha256"):
        raise RuntimeError("image Visual QA/canon binding invalid")
    qa_rows = {row["asset"]: row for row in qa.get("assets", [])}
    for asset, path in (("left_panel", LEFT), ("right_panel", RIGHT)):
        if qa_rows.get(asset, {}).get("verified") is not True or qa_rows[asset].get("sha256") != sha(path):
            raise RuntimeError(f"image QA hash mismatch: {asset}")

    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    temp = OUTPUT.with_name(OUTPUT.stem + ".part.png")
    if temp.exists():
        temp.unlink()
    filtergraph = (
        "[0:v]scale=656:1080:force_original_aspect_ratio=decrease:flags=lanczos,"
        "pad=656:1080:(ow-iw)/2:(oh-ih)/2:color=0x101820[left];"
        "[1:v]scale=656:1080:force_original_aspect_ratio=decrease:flags=lanczos,"
        "pad=656:1080:(ow-iw)/2:(oh-ih)/2:color=0x101820[right];"
        "color=c=0x101820:s=608x1080:r=1[center];"
        "[left][center][right]hstack=inputs=3,format=rgb24[out]"
    )
    subprocess.run(
        ["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-i", str(LEFT), "-i", str(RIGHT),
         "-filter_complex", filtergraph, "-map", "[out]", "-frames:v", "1", "-threads", "1", str(temp)],
        check=True,
    )
    stream = probe(temp)
    if stream.get("width") != 1920 or stream.get("height") != 1080 or temp.stat().st_size < 100000:
        raise RuntimeError(f"local layout probe failed: {stream}")
    os.replace(temp, OUTPUT)
    stream = probe(OUTPUT)
    receipt = {
        "version": 2,
        "status": "built_pending_visual_qa",
        "verified": True,
        "source_canon_sha256": promotion["canon_sha256"],
        "builder_location": "agent_local",
        "builder_tool": {
            "name": "FFmpeg",
            "filters": "scale(force_original_aspect_ratio=decrease,flags=lanczos)+pad+color+hstack",
            "pixel_format": "rgb24",
            "background_rgb": [16, 24, 32],
        },
        "operation": "local static-image normalize/resample/crop/canvas composition only",
        "remote_layout_build_forbidden": True,
        "source_panels": {
            "left": {"path": str(LEFT.relative_to(PROJECT)), "sha256": sha(LEFT), "probe": probe(LEFT)},
            "right": {"path": str(RIGHT.relative_to(PROJECT)), "sha256": sha(RIGHT), "probe": probe(RIGHT)},
        },
        "output": {"path": str(OUTPUT.relative_to(PROJECT)), "sha256": sha(OUTPUT), "bytes": OUTPUT.stat().st_size, "probe": stream},
        "canvas": {"width": 1920, "height": 1080},
        "regions": {
            "left": {"x": 0, "y": 0, "width": 656, "height": 1080},
            "center": {"x": 656, "y": 0, "width": 608, "height": 1080, "clean": True},
            "right": {"x": 1264, "y": 0, "width": 656, "height": 1080},
        },
        "image_visual_qa_receipt": str(IMAGE_QA.relative_to(PROJECT)),
        "image_visual_qa_sha256": sha(IMAGE_QA),
        "visual_qa": {"verified": False, "status": "pending"},
        "built_at": datetime.now(timezone.utc).isoformat(),
    }
    atomic_json(RECEIPT, receipt)
    print(json.dumps({"verified": True, "status": receipt["status"], "builder_location": "agent_local", "output": str(OUTPUT), "sha256": receipt["output"]["sha256"], "probe": stream}, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as exc:
        print("Local layout blocked: " + str(exc), file=sys.stderr)
        sys.exit(1)
