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

import argparse
import json
import re
import uuid
import zipfile
from pathlib import Path


SENSITIVE_PATTERNS = (
    re.compile(r"密码"),
    re.compile(r"账号[:：]"),
    re.compile(r"https?://.*\.apk", re.I),
    re.compile(r"cookie", re.I),
)


def stable_id(*parts: str) -> str:
    return str(uuid.uuid5(uuid.NAMESPACE_URL, "|".join(parts)))


def topic(sku: str, path: tuple[str, ...], title: str, children: list[dict] | None = None) -> dict:
    node = {"id": stable_id(sku, *path, title), "title": title}
    if children:
        node["children"] = {"attached": children}
    return node


def branch(sku: str, name: str, values: list[str]) -> dict:
    return topic(sku, (name,), name, [topic(sku, (name, str(i)), value) for i, value in enumerate(values, 1)])


def validate_manifest(data: dict) -> None:
    required = {
        "sku", "name", "buyer", "one_liner", "primary_buying_point", "suitable_for",
        "standard_combo", "core_modules", "main_flow", "customer_values", "boundaries",
        "forbidden_terms", "infographic_focus",
    }
    missing = sorted(required - set(data))
    if missing:
        raise ValueError(f"missing fields: {', '.join(missing)}")
    if len(data["customer_values"]) != 3 or len(data["boundaries"]) != 3:
        raise ValueError("customer_values and boundaries must each contain exactly 3 items")
    if not 4 <= len(data["main_flow"]) <= 6:
        raise ValueError("main_flow must contain 4-6 ordered steps")
    if len(data["core_modules"]) != 4:
        raise ValueError("core_modules must contain exactly 4 modules")
    scannable = {key: value for key, value in data.items() if key != "forbidden_terms"}
    raw = json.dumps(scannable, ensure_ascii=False)
    for pattern in SENSITIVE_PATTERNS:
        if pattern.search(raw):
            raise ValueError(f"sensitive content matched: {pattern.pattern}")
    for forbidden in data["forbidden_terms"]:
        if forbidden and any(forbidden in step for step in data["main_flow"]):
            raise ValueError(f"forbidden term appears in main flow: {forbidden}")


def make_sheet(data: dict, edition: str) -> list[dict]:
    sku = data["sku"]
    if edition == "internal":
        module_nodes = []
        for module in data["core_modules"]:
            module_nodes.append(topic(sku, ("核心功能", module["name"]), module["name"], [
                topic(sku, ("核心功能", module["name"], str(i)), value)
                for i, value in enumerate(module["points"], 1)
            ]))
        branches = [
            branch(sku, "产品定位", [data["one_liner"], f"购买人：{data['buyer']}", data["primary_buying_point"]]),
            branch(sku, "适用客户", data["suitable_for"]),
            branch(sku, "标准组合", data["standard_combo"]),
            topic(sku, ("核心功能",), "核心功能", module_nodes),
            branch(sku, "使用主流程", data["main_flow"]),
            branch(sku, "客户价值", data["customer_values"]),
            branch(sku, "产品边界", data["boundaries"]),
        ]
        sheet_title = "产品极简版"
    else:
        branches = [
            branch(sku, "一句话产品", [data["one_liner"]]),
            branch(sku, "适合谁", data["suitable_for"]),
            branch(sku, "标准组合", data["standard_combo"]),
            branch(sku, "使用主流程", data["main_flow"]),
            branch(sku, "客户价值", data["customer_values"]),
            branch(sku, "产品边界", data["boundaries"]),
        ]
        sheet_title = "销售极简版"

    root_title = data.get("release_name") or f"{sku} {data['name']}"
    root = {
        "id": stable_id(sku, edition, "root"),
        "class": "topic",
        "title": root_title,
        "structureClass": "org.xmind.ui.logic.right",
        "children": {"attached": branches},
    }
    return [{
        "id": stable_id(sku, edition, "sheet"),
        "revisionId": stable_id(sku, edition, "revision"),
        "class": "sheet",
        "rootTopic": root,
        "title": sheet_title,
        "topicOverlapping": "overlap",
    }]


def write_xmind(path: Path, content: list[dict]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    metadata = {"dataStructureVersion": "3", "creator": {"name": "Codex", "version": "1.0"}, "layoutEngineVersion": "5"}
    manifest = {"file-entries": {"content.json": {}, "metadata.json": {}}}
    with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
        archive.writestr("content.json", json.dumps(content, ensure_ascii=False, indent=2))
        archive.writestr("metadata.json", json.dumps(metadata, ensure_ascii=False, separators=(",", ":")))
        archive.writestr("manifest.json", json.dumps(manifest, ensure_ascii=False, separators=(",", ":")))


def main() -> int:
    parser = argparse.ArgumentParser(description="Build internal-minimal and sales-minimal XMind files from one SKU manifest.")
    parser.add_argument("--manifest", required=True, type=Path)
    parser.add_argument("--output-dir", required=True, type=Path)
    parser.add_argument("--date", default="20260901")
    args = parser.parse_args()

    data = json.loads(args.manifest.read_text(encoding="utf-8"))
    validate_manifest(data)
    release_code = data.get("release_code", data["sku"])
    internal = args.output_dir / f"{release_code}-产品极简版.xmind"
    sales = args.output_dir / f"{release_code}-销售极简版.xmind"
    write_xmind(internal, make_sheet(data, "internal"))
    write_xmind(sales, make_sheet(data, "sales"))
    print(json.dumps({"internal": str(internal), "sales": str(sales)}, ensure_ascii=False))
    return 0


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