#!/usr/bin/env python3
"""Summarize map layer tile pairs for collision-rule analysis."""
from __future__ import annotations

import argparse
import json
import re
from collections import Counter
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]


def load_maps(path: Path) -> dict:
    text = path.read_text(encoding="utf-8")
    match = re.fullmatch(r"window\.HWANSE_MAPS = (.*);\n?", text, re.S)
    if not match:
        raise ValueError(f"{path} does not contain the expected wrapper")
    return json.loads(match.group(1))


def passable_counts(layer0: list[int], layer1: list[int], terrain_blocked: set[int]) -> dict[str, int]:
    return {
        "terrainCandidate": sum(1 for a, b in zip(layer0, layer1) if a not in terrain_blocked and b == 0),
        "darkTerrainCandidate": sum(1 for a, b in zip(layer0, layer1) if a in terrain_blocked and b == 0),
        "walkCandidate": sum(1 for a, b in zip(layer0, layer1) if a > 1 and b == 0),
        "layer0NonEmpty": sum(1 for value in layer0 if value > 1),
        "layer1Empty": sum(1 for value in layer1 if value == 0),
        "layer0": sum(1 for value in layer0 if value != 0),
        "layer1": sum(1 for value in layer1 if value != 0),
        "both": sum(1 for a, b in zip(layer0, layer1) if a != 0 and b != 0),
        "either": sum(1 for a, b in zip(layer0, layer1) if a != 0 or b != 0),
    }


def summarize_map(name: str, info: dict, limit: int) -> dict:
    layer0, layer1 = info["layers"]
    terrain_blocked = set(info.get("terrainBlockedTiles") or [])
    pairs = Counter(zip(layer0, layer1))
    nonmatching = sum(count for (a, b), count in pairs.items() if a != b)
    return {
        "name": name,
        "width": info["width"],
        "height": info["height"],
        "tileCount": info["width"] * info["height"],
        "tilesets": info.get("layerTilesets", []),
        "sceneIdHex": info.get("sceneIdHex"),
        "uniqueLayer0": len(set(layer0)),
        "uniqueLayer1": len(set(layer1)),
        "uniquePairs": len(pairs),
        "nonmatchingPairs": nonmatching,
        "terrainBlockedTileCount": len(terrain_blocked),
        "passableCounts": passable_counts(layer0, layer1, terrain_blocked),
        "commonPairs": [
            {"count": count, "layer0": a, "layer1": b}
            for (a, b), count in pairs.most_common(limit)
        ],
    }


def markdown(summaries: list[dict], limit: int) -> str:
    lines = [
        "# Map Tile Summary",
        "",
        "Generated from `out/maps.js` for collision-rule analysis.",
        "",
        "| map | size | tilesets | terrain blocked | unique L0/L1/pairs | nonmatching | passable bright/dark/walk/L0>1/L1=0/L0/L1/both/either | common pairs |",
        "| --- | ---: | --- | ---: | ---: | ---: | ---: | --- |",
    ]
    for item in summaries:
        counts = item["passableCounts"]
        common = ", ".join(
            f"{pair['count']}x({pair['layer0']},{pair['layer1']})"
            for pair in item["commonPairs"][:limit]
        )
        lines.append(
            f"| {item['name']} | {item['width']}x{item['height']} | "
            f"{','.join(item['tilesets'])} | "
            f"{item['terrainBlockedTileCount']} | "
            f"{item['uniqueLayer0']}/{item['uniqueLayer1']}/{item['uniquePairs']} | "
            f"{item['nonmatchingPairs']} | "
            f"{counts['terrainCandidate']}/{counts['darkTerrainCandidate']}/{counts['walkCandidate']}/"
            f"{counts['layer0NonEmpty']}/{counts['layer1Empty']}/"
            f"{counts['layer0']}/{counts['layer1']}/{counts['both']}/{counts['either']} | "
            f"{common} |"
        )
    lines.append("")
    return "\n".join(lines)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--maps", type=Path, default=ROOT / "out" / "maps.js")
    parser.add_argument("--out", type=Path)
    parser.add_argument("--json-out", type=Path)
    parser.add_argument("--limit", type=int, default=8)
    args = parser.parse_args()

    maps = load_maps(args.maps)
    summaries = [
        summarize_map(name, info, args.limit)
        for name, info in sorted(maps.items())
    ]

    if args.out:
        args.out.parent.mkdir(parents=True, exist_ok=True)
        args.out.write_text(markdown(summaries, args.limit), encoding="utf-8")
    if args.json_out:
        args.json_out.parent.mkdir(parents=True, exist_ok=True)
        args.json_out.write_text(json.dumps(summaries, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

    if args.out:
        print(f"wrote {len(summaries)} map tile summaries -> {args.out}")
    else:
        print(f"built {len(summaries)} map tile summaries")
    if args.json_out:
        print(f"wrote JSON -> {args.json_out}")


if __name__ == "__main__":
    main()
