"""Guard the invariants that silently break the toolset (AGENTS.md §5, §2.3):

    python test_namesync.py      # asserts; no nwn_gff / Blender needed

1. Every .itp Features leaf is backed by a matching .set [GROUP#] whose first tile
   is that leaf's model (Name + Tile0).
2. Paint tiles (fills + terrain transitions) stay OUT of every group/feature, so the
   brush pool is non-empty — an empty pool crashes the New Area wizard (the
   2026-07-04 access violation). Regression guard for that crash.
3. The cobble/canal paint matrix covers every corner pattern the brush can request
   (an incomplete matrix = paint crash).
4. The .itp Terrain folder exposes exactly the paintable terrains + eraser.
Runs on make_set.build() and make_itp.build() directly, so it fails fast if
group_name / resref / is_paint / placements drift apart.
"""
import re

import tiles
import make_set
import make_itp


def _groups(setf):
    return {m.group(1): int(m.group(2))
            for m in re.finditer(r"\[GROUP\d+\]\nName=(.+?)\n(?:.*\n)*?Tile0=(\d+)", setf)}


def _tile_models(setf):
    return {int(m.group(1)): m.group(2)
            for m in re.finditer(r"\[TILE(\d+)\]\nModel=(\S+)", setf)}


def _features():
    return [l for c in make_itp.build()["MAIN"]["value"] if c["ID"]["value"] == 0
            for l in c["LIST"]["value"]]


def test_features_back_every_group():
    setf = "\n".join(make_set.build())
    groups, models = _groups(setf), _tile_models(setf)
    feat = _features()
    nonpaint = [s for s in tiles.TILES if not tiles.is_paint(s)]
    assert len(feat) == len(groups) == len(nonpaint), \
        f"count mismatch: {len(feat)} features / {len(groups)} groups / {len(nonpaint)} non-paint"
    for lf in feat:
        name, resref = lf["NAME"]["value"], lf["RESREF"]["value"]
        assert name in groups, f".itp feature {name!r} has no matching .set [GROUP#]"
        tile0 = groups[name]
        assert models[tile0] == resref, \
            f"{name}: group Tile0={tile0} model {models[tile0]!r} != leaf RESREF {resref!r}"


def test_paint_tiles_stay_in_brush_pool():
    paint = [s for s in tiles.TILES if tiles.is_paint(s)]
    assert paint, "no paint tiles -> empty brush pool -> New Area crash"
    grouped = set(_groups("\n".join(make_set.build())))
    featured = {l["NAME"]["value"] for l in _features()}
    for s in paint:
        gn = tiles.group_name(s)
        assert gn not in grouped, f"paint tile {gn!r} is grouped -> dropped from brush pool"
        assert gn not in featured, f"paint tile {gn!r} is a feature -> dropped from brush pool"


def test_canal_matrix_complete():
    want, _covered, missing = tiles.canal_paint_matrix()
    assert not missing, f"paint matrix missing {len(missing)} pattern(s): {sorted(missing)}"
    assert want, "empty matrix?"


def test_terrain_folder_is_paintables_plus_eraser():
    terr = [l for c in make_itp.build()["MAIN"]["value"] if c["ID"]["value"] == 2
            for l in c["LIST"]["value"]]
    names = {l["RESREF"]["value"] for l in terr}
    assert names == {"cobble", "canal", "eraser"}, names


if __name__ == "__main__":
    test_features_back_every_group()
    test_paint_tiles_stay_in_brush_pool()
    test_canal_matrix_complete()
    test_terrain_folder_is_paintables_plus_eraser()
    print("namesync OK")
