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

import argparse
import json
import struct
import zipfile
from pathlib import Path


def content_from_xmind(path: Path) -> object:
    with zipfile.ZipFile(path) as archive:
        return json.loads(archive.read("content.json"))


def titles_from_content(content: object) -> list[str]:
    titles: list[str] = []

    def walk(value: object) -> None:
        if isinstance(value, dict):
            title = value.get("title")
            if isinstance(title, str):
                titles.append(title)
            for child in value.values():
                walk(child)
        elif isinstance(value, list):
            for child in value:
                walk(child)

    walk(content)
    return titles


def flow_from_content(content: object) -> list[str]:
    found: list[str] = []

    def walk(value: object) -> None:
        if isinstance(value, dict):
            if value.get("title") == "使用主流程":
                attached = value.get("children", {}).get("attached", [])
                found.extend(node.get("title", "") for node in attached if isinstance(node, dict))
                return
            for child in value.values():
                walk(child)
        elif isinstance(value, list):
            for child in value:
                walk(child)

    walk(content)
    return found


def png_size(path: Path) -> tuple[int, int]:
    with path.open("rb") as handle:
        header = handle.read(24)
    if header[:8] != b"\x89PNG\r\n\x1a\n":
        raise ValueError(f"not a PNG: {path}")
    return struct.unpack(">II", header[16:24])


def main() -> int:
    parser = argparse.ArgumentParser(description="Validate final smart-canteen SKU release structure and content.")
    parser.add_argument("--root", required=True, type=Path)
    parser.add_argument("--manifests", required=True, type=Path)
    parser.add_argument("--external-only", action="store_true", help="Validate a simplified external package containing only final assets")
    args = parser.parse_args()
    final_root = args.root / "01-最终呈现"
    required_roots = [final_root] if args.external_only else [final_root, args.root / "02-NotebookLM资料", args.root / "03-历史版本"]
    errors: list[str] = []
    for required in required_roots:
        if not required.is_dir():
            errors.append(f"missing directory: {required}")

    results = []
    for manifest_path in sorted(args.manifests.glob("*.json")):
        data = json.loads(manifest_path.read_text(encoding="utf-8"))
        sku_dir = final_root / data.get("release_name", f"{data['sku']}_{data['name']}")
        files = [path for path in sku_dir.iterdir() if path.is_file()] if sku_dir.is_dir() else []
        xminds = sorted(path for path in files if path.suffix.lower() == ".xmind")
        pngs = sorted(path for path in files if path.suffix.lower() == ".png")
        others = [path for path in files if path not in xminds and path not in pngs]
        if len(xminds) != 2:
            errors.append(f"{data['sku']}: expected 2 XMind files, found {len(xminds)}")
        if len(pngs) != 1:
            errors.append(f"{data['sku']}: expected 1 PNG, found {len(pngs)}")
        if others:
            errors.append(f"{data['sku']}: unexpected final files: {[path.name for path in others]}")
        for xmind in xminds:
            content = content_from_xmind(xmind)
            titles = titles_from_content(content)
            flow = flow_from_content(content)
            node_limit = 28 if "销售极简版" in xmind.name else 42
            if len(titles) > node_limit:
                errors.append(f"{data['sku']}: {xmind.name} has {len(titles)} titled nodes, limit {node_limit}")
            if flow != data["main_flow"]:
                errors.append(f"{data['sku']}: flow mismatch in {xmind.name}: {flow}")
            joined = "\n".join(titles)
            for forbidden in data["forbidden_terms"]:
                if forbidden and forbidden in joined:
                    errors.append(f"{data['sku']}: forbidden term {forbidden} in {xmind.name}")
        image_size = None
        if pngs:
            image_size = png_size(pngs[0])
            if image_size[0] <= image_size[1]:
                errors.append(f"{data['sku']}: infographic is not landscape: {image_size}")
        results.append({"sku": data["sku"], "files": [path.name for path in files], "png_size": image_size})

    report = {"status": "PASS" if not errors else "FAIL", "results": results, "errors": errors}
    print(json.dumps(report, ensure_ascii=False, indent=2))
    return 0 if not errors else 1


if __name__ == "__main__":
    raise SystemExit(main())
