#!/usr/bin/env python3
"""Validate, merge, and export accepted CNS map layout review data."""
from __future__ import annotations

import argparse
import json
from pathlib import Path

from summarize_map_tiles import load_maps


ROOT = Path(__file__).resolve().parents[1]
DATA = ROOT / "data"
OUT = ROOT / "out"

STATES = {"accepted", "rejected"}
LAYOUTS = {"planar_layers"}


def normalize(data: object, maps: dict) -> dict[str, dict]:
    if not isinstance(data, dict):
        raise ValueError("map layout review data must be an object")
    normalized = {}
    for key, value in data.items():
        if key not in maps:
            raise ValueError(f"{key}: unknown map")
        if not isinstance(value, dict):
            raise ValueError(f"{key}: review entry must be an object")
        state = value.get("state")
        if state not in STATES:
            raise ValueError(f"{key}: state must be one of {sorted(STATES)}")
        layout = value.get("layout")
        if layout not in LAYOUTS:
            raise ValueError(f"{key}: unsupported layout {layout!r}")
        tilesets = value.get("tilesets")
        if not isinstance(tilesets, list) or len(tilesets) != len(maps[key].get("layers", [])):
            raise ValueError(f"{key}: layout review tilesets must match map layer count")
        normalized[key] = {
            "map": key,
            "layout": layout,
            "state": state,
            "tilesets": tilesets,
        }
        source = value.get("source")
        if isinstance(source, str) and source.strip():
            normalized[key]["source"] = source.strip()
        note = value.get("note")
        if isinstance(note, str) and note.strip():
            normalized[key]["note"] = note.strip()
    return dict(sorted(normalized.items()))


def load_reviews(path: Path, maps: dict) -> dict[str, dict]:
    if not path.exists():
        return {}
    return normalize(json.loads(path.read_text(encoding="utf-8")), maps)


def merge(base: dict[str, dict], patch: dict[str, dict]) -> dict[str, dict]:
    merged = dict(base)
    merged.update(patch)
    return dict(sorted(merged.items()))


def planar_defaults(maps: dict) -> dict[str, dict]:
    defaults = {}
    for map_name, info in sorted(maps.items()):
        defaults[map_name] = {
            "map": map_name,
            "layout": "planar_layers",
            "state": "accepted",
            "tilesets": info.get("layerTilesets") or [info.get("tileset")],
            "source": "cns-body-planar-confirmed",
            "note": "CNS map body stores layer0 block followed by layer1 flags/occlusion block.",
        }
    return defaults


def coverage_rows(maps: dict, reviews: dict[str, dict]) -> list[dict]:
    rows = []
    for map_name, info in sorted(maps.items()):
        review = reviews.get(map_name)
        default_tilesets = info.get("layerTilesets") or [info.get("tileset")]
        rows.append({
            "map": map_name,
            "state": review.get("state") if review else "unreviewed",
            "layout": review.get("layout") if review else "planar_layers",
            "tilesets": review.get("tilesets") if review else default_tilesets,
            "defaultTilesets": default_tilesets,
            "size": f"{info['width']}x{info['height']}",
            "visual": "layer1UnderSceneTilesets",
        })
    return rows


def markdown(reviews: dict[str, dict]) -> str:
    lines = [
        "# Map Layout Reviews",
        "",
        "Generated from `data/map_layout_reviews.json` via `tools/map_layout_reviews.py`.",
        "",
        "| map | layout | state | tilesets | source | note | open |",
        "| --- | --- | --- | --- | --- | --- | --- |",
    ]
    if not reviews:
        lines.append("| - | - | - | - | - | - | - |")
    for review in reviews.values():
        map_name = review["map"]
        layout = review["layout"]
        tilesets = ",".join(review["tilesets"])
        link = (
            f"../web/game.html?map={map_name}&debugRender=1&visualMode=layer1UnderSceneTilesets"
            f"&tilesets={tilesets}&layoutMode={layout}&events=1&overview=1"
        )
        lines.append(
            f"| {map_name} | `{layout}` | {review['state']} | `{tilesets}` | "
            f"{review.get('source', '-')} | {review.get('note', '-')} | [open]({link}) |"
        )
    lines.append("")
    return "\n".join(lines)


def write_outputs(reviews: dict[str, dict], out_dir: Path) -> None:
    out_dir.mkdir(parents=True, exist_ok=True)
    (out_dir / "map_layout_reviews.json").write_text(
        json.dumps(reviews, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    (out_dir / "map_layout_reviews.js").write_text(
        "window.HWANSE_MAP_LAYOUT_REVIEWS = "
        + json.dumps(reviews, ensure_ascii=False, separators=(",", ":"))
        + ";\n",
        encoding="utf-8",
    )


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--src", type=Path, default=DATA / "map_layout_reviews.json")
    parser.add_argument("--maps", type=Path, default=OUT / "maps.js")
    parser.add_argument("--out", type=Path, default=OUT)
    parser.add_argument("--merge", type=Path, help="merge browser-exported map layout review JSON into --src")
    parser.add_argument("--accept-planar-defaults", action="store_true", help="add accepted planar layout reviews for maps missing from --src")
    parser.add_argument("--write-src", action="store_true", help="write merged reviews back to --src")
    args = parser.parse_args()

    maps = load_maps(args.maps)
    reviews = load_reviews(args.src, maps)
    if args.merge:
        patch = load_reviews(args.merge, maps)
        reviews = normalize(merge(reviews, patch), maps)
    if args.accept_planar_defaults:
        defaults = {
            key: value
            for key, value in planar_defaults(maps).items()
            if key not in reviews
        }
        reviews = normalize(merge(defaults, reviews), maps)
    if (args.merge or args.accept_planar_defaults) and args.write_src:
        args.src.write_text(json.dumps(reviews, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
        print(f"updated source map layout reviews -> {args.src}")
    write_outputs(reviews, args.out)
    print(f"wrote {len(reviews)} map layout reviews -> {args.out / 'map_layout_reviews.json'}")


if __name__ == "__main__":
    main()
