"""Blender headless script: extract asset catalog from a .blend library file.

Usage:
    blender --background --python catalog_assets.py -- <blend_path>

Outputs a single JSON line to stdout:
    {"materials": [{"name": "Mat1", "base_color": [R,G,B], "metallic": 0.0, ...}, ...],
     "node_groups": ["NG1", ...]}

Only assets marked via Blender's asset system (asset_data is not None) are included.
PBR properties are extracted from the Principled BSDF node of each material.
"""
from __future__ import annotations

import json
import math
import sys
import traceback


def _linear_to_srgb(v: float) -> float:
    """Convert a single linear color channel to sRGB."""
    if v <= 0.0031308:
        return v * 12.92
    return 1.055 * math.pow(v, 1.0 / 2.4) - 0.055


def _extract_pbr(mat) -> dict:
    """Extract PBR properties from a Blender material's Principled BSDF node.

    Returns dict with: name, base_color (sRGB), metallic, roughness, transmission, ior.
    Falls back to diffuse_color if no Principled BSDF found.
    """
    entry = {
        "name": mat.name,
        "base_color": [0.5, 0.5, 0.5],
        "metallic": 0.0,
        "roughness": 0.5,
        "transmission": 0.0,
        "ior": 1.45,
    }

    # Find Principled BSDF node
    bsdf = None
    if mat.use_nodes and mat.node_tree:
        for node in mat.node_tree.nodes:
            if node.type == "BSDF_PRINCIPLED":
                bsdf = node
                break

    if bsdf:
        # Base Color — convert linear → sRGB
        bc = bsdf.inputs["Base Color"].default_value
        entry["base_color"] = [
            round(_linear_to_srgb(bc[0]), 4),
            round(_linear_to_srgb(bc[1]), 4),
            round(_linear_to_srgb(bc[2]), 4),
        ]
        entry["metallic"] = round(bsdf.inputs["Metallic"].default_value, 4)
        entry["roughness"] = round(bsdf.inputs["Roughness"].default_value, 4)
        # Transmission Weight (Blender 4.0+) or Transmission (older)
        for tx_name in ("Transmission Weight", "Transmission"):
            if tx_name in bsdf.inputs:
                entry["transmission"] = round(bsdf.inputs[tx_name].default_value, 4)
                break
        if "IOR" in bsdf.inputs:
            entry["ior"] = round(bsdf.inputs["IOR"].default_value, 4)
    else:
        # Fallback: use viewport diffuse_color (already sRGB)
        dc = mat.diffuse_color
        entry["base_color"] = [round(dc[0], 4), round(dc[1], 4), round(dc[2], 4)]

    return entry


def main() -> None:
    argv = sys.argv
    if "--" not in argv:
        print(json.dumps({"error": "No blend path provided after --"}))
        sys.exit(1)

    blend_path = argv[argv.index("--") + 1]

    import bpy  # type: ignore[import]

    bpy.ops.wm.open_mainfile(filepath=blend_path)

    materials = [
        _extract_pbr(m) for m in bpy.data.materials if m.asset_data is not None
    ]
    node_groups = [ng.name for ng in bpy.data.node_groups if ng.asset_data is not None]

    catalog = {"materials": materials, "node_groups": node_groups}
    print(json.dumps(catalog))


try:
    main()
except SystemExit:
    raise
except Exception:
    traceback.print_exc()
    sys.exit(1)
