"""Build the Westgate_Urban_Foundation_MVP Blender scene (run in Blender 4.0.2).

    blender-4.0.2 --background --python build.py -- out.blend

Realises tiles.py: one Collection per tile (VIS_/WOK_/BLOCK_ sub-objects + edge
custom props), a TILE_EDGE_METADATA text block, and the two preview assemblies.
Geometry is intentionally simple (KICKOFF.md: a geometry/grammar prototype).
"""

import json
import os
import sys

import bpy

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import tiles  # noqa: E402


def get_mat(name):
    m = bpy.data.materials.get(name)
    if m:
        return m
    m = bpy.data.materials.new(name)
    r, g, b = tiles.COLORS.get(name, (0.5, 0.5, 0.5))
    m.diffuse_color = (r, g, b, 1.0)                 # viewport (solid) colour
    m.use_nodes = True
    bsdf = m.node_tree.nodes.get("Principled BSDF")
    if bsdf:
        bsdf.inputs["Base Color"].default_value = (r, g, b, 1.0)
    return m


def build_vis(spec):
    """Subdivided 10x10 plane (100 quads): per-face material via paint(), per-vertex Z."""
    n, step = 10, 1.0
    paint = tiles.make_paint(spec)
    verts, vidx = [], {}

    def vi(i, j):
        if (i, j) not in vidx:
            x, y = -5 + i * step, -5 + j * step
            vidx[(i, j)] = len(verts)
            verts.append((x, y, tiles.height(spec, x, y)))
        return vidx[(i, j)]

    faces, face_mat, mats, midx = [], [], [], {}
    for i in range(n):
        for j in range(n):
            faces.append((vi(i, j), vi(i + 1, j), vi(i + 1, j + 1), vi(i, j + 1)))
            cx, cy = -5 + (i + 0.5) * step, -5 + (j + 0.5) * step
            mn = paint(cx, cy)
            if mn not in midx:
                midx[mn] = len(mats)
                mats.append(mn)
            face_mat.append(midx[mn])

    me = bpy.data.meshes.new("VIS_" + spec["code"])
    me.from_pydata(verts, [], faces)
    for mn in mats:
        me.materials.append(get_mat(mn))
    for poly, mi in zip(me.polygons, face_mat):
        poly.material_index = mi
    me.update()
    ob = bpy.data.objects.new("VIS_" + spec["code"], me)
    n_, e_, s_, w_ = spec["edges"]
    ob["edge_N"], ob["edge_E"], ob["edge_S"], ob["edge_W"] = n_, e_, s_, w_
    return ob


def build_quads(name, quads, matname):
    if not quads:
        return None
    verts, faces = [], []
    for q in quads:
        b = len(verts)
        verts += q
        faces.append((b, b + 1, b + 2, b + 3))
    me = bpy.data.meshes.new(name)
    me.from_pydata(verts, [], faces)
    me.materials.append(get_mat(matname))
    me.update()
    return bpy.data.objects.new(name, me)


def build_tile(spec):
    col = bpy.data.collections.new(f"{spec['code']}_{spec['name']}")
    bpy.context.scene.collection.children.link(col)
    col.objects.link(build_vis(spec))
    quads = tiles.wok_quads(spec)
    wok = build_quads("WOK_" + spec["code"], [q for q, w in quads if w], "MAT_debug_walkmesh")
    blk = build_quads("BLOCK_" + spec["code"], [q for q, w in quads if not w], "MAT_debug_blocker")
    if wok:
        col.objects.link(wok)
    if blk:
        col.objects.link(blk)
    return col


def instance(col, loc, parent, name):
    ob = bpy.data.objects.new(name, None)
    ob.instance_type = "COLLECTION"
    ob.instance_collection = col
    ob.location = loc
    parent.objects.link(ob)


def label(text, loc, parent):
    cu = bpy.data.curves.new(text, "FONT")
    cu.body = text
    cu.size = 1.4
    ob = bpy.data.objects.new("LBL_" + text, cu)
    ob.location = loc
    parent.objects.link(ob)


def main():
    argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
    out = os.path.abspath(argv[0]) if argv else os.path.abspath("westgate_urban_foundation_mvp.blend")

    bpy.ops.wm.read_factory_settings(use_empty=True)
    bpy.context.scene.unit_settings.system = "METRIC"

    tilecol = {s["code"]: build_tile(s) for s in tiles.TILES}

    # edge metadata text block (KICKOFF.md)
    txt = bpy.data.texts.new("TILE_EDGE_METADATA")
    for s in tiles.TILES:
        n_, e_, s_, w_ = s["edges"]
        txt.write(f"{s['code']}_{s['name']}\nN: {n_}\nE: {e_}\nS: {s_}\nW: {w_}\n\n")

    # library grid preview (8 wide, 12 m pitch, labelled)
    grid = bpy.data.collections.new("PREVIEW_tile_library_grid")
    bpy.context.scene.collection.children.link(grid)
    cols = 8
    for i, s in enumerate(tiles.TILES):
        gx, gy = (i % cols) * 12, -(i // cols) * 12
        instance(tilecol[s["code"]], (gx, gy, 0), grid, "GRID_" + s["code"])
        label(s["code"], (gx - 4.5, gy + 5.5, 0), grid)

    # 12x12 test district (10 m pitch), row south-ward, col east-ward
    dist = bpy.data.collections.new("PREVIEW_12x12_test_district")
    bpy.context.scene.collection.children.link(dist)
    skipped = []
    for r, row in enumerate(tiles.MAP):
        for c, code in enumerate(row):
            tc = tilecol.get(code)
            if tc is None:
                skipped.append([r, c, code])
                continue
            instance(tc, (c * 10, -r * 10, 0), dist, f"D_{r:02d}_{c:02d}_{code}")

    bpy.ops.wm.save_as_mainfile(filepath=out)

    report = {
        "blend": out,
        "tile_collections": len(tilecol),
        "district_cells": sum(len(r) for r in tiles.MAP),
        "district_skipped": skipped,
        "materials": len(tiles.COLORS),
    }
    print("<<<WGT_REPORT_BEGIN>>>" + json.dumps(report, indent=2) + "<<<WGT_REPORT_END>>>")


if __name__ == "__main__":
    main()
