"""GLB import and geometry helpers for Blender headless renders."""
from __future__ import annotations

import math
import sys


def import_glb(glb_file: str) -> list:
    """Import OCC-generated GLB into Blender.

    OCC exports one mesh object per STEP part, already in metres.
    Blender's native GLTF importer preserves part names.

    Returns list of Blender mesh objects, centred at world origin.
    """
    import bpy  # type: ignore[import]
    from mathutils import Vector  # type: ignore[import]

    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.import_scene.gltf(filepath=glb_file)
    parts = [o for o in bpy.context.selected_objects if o.type == 'MESH']

    if not parts:
        print(f"ERROR: No mesh objects imported from {glb_file}")
        sys.exit(1)

    print(f"[blender_render] imported {len(parts)} part(s) from GLB: "
          f"{[p.name for p in parts[:5]]}")

    # Remove OCC-baked custom normals so shade_smooth_by_angle can recompute
    # normals from scratch (respecting our sharp edge marks).
    cleared = 0
    for p in parts:
        if "custom_normal" in p.data.attributes:
            p.data.attributes.remove(p.data.attributes["custom_normal"])
            cleared += 1
    if cleared:
        print(f"[blender_render] cleared OCC custom_normal from {cleared} mesh objects")

    # Centre combined bbox at world origin
    all_corners = []
    for p in parts:
        all_corners.extend(p.matrix_world @ Vector(c) for c in p.bound_box)

    if all_corners:
        mins = Vector((min(v.x for v in all_corners),
                       min(v.y for v in all_corners),
                       min(v.z for v in all_corners)))
        maxs = Vector((max(v.x for v in all_corners),
                       max(v.y for v in all_corners),
                       max(v.z for v in all_corners)))
        center = (mins + maxs) * 0.5
        # Move root objects (parentless) to centre. Adjusting a child's local
        # .location by a world-space vector gives wrong results when the GLB has
        # Empty parent nodes (OCC assembly hierarchy). Shifting the root moves
        # the entire hierarchy correctly.
        all_imported = list(bpy.context.selected_objects)
        root_objects = [o for o in all_imported if o.parent is None]
        for obj in root_objects:
            obj.location -= center

    return parts


def apply_rotation(parts: list, rx: float, ry: float, rz: float) -> None:
    """Apply Euler rotation (degrees, XYZ order) to all parts around world origin.

    After import_glb the combined bbox center is at world origin,
    so rotating around origin is equivalent to rotating around the assembly center.
    """
    if not parts or (rx == 0.0 and ry == 0.0 and rz == 0.0):
        return
    import bpy  # type: ignore[import]
    from mathutils import Euler  # type: ignore[import]

    rot_mat = Euler((math.radians(rx), math.radians(ry), math.radians(rz)), 'XYZ').to_matrix().to_4x4()
    for p in parts:
        p.matrix_world = rot_mat @ p.matrix_world
    # Bake rotation into mesh data so camera bbox calculations see the rotated geometry
    bpy.ops.object.select_all(action='DESELECT')
    for p in parts:
        p.select_set(True)
    bpy.context.view_layer.objects.active = parts[0]
    bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
    print(f"[blender_render] applied rotation ({rx}°, {ry}°, {rz}°) to {len(parts)} parts")


def import_usd_file(usd_path: str) -> tuple[list, dict]:
    """Import USD stage into current Blender scene — delegates to import_usd module.

    Returns (parts, material_lookup) where material_lookup maps
    blender_object_name → canonical SCHAEFFLER material name (from USD primvars).
    """
    from import_usd import import_usd_file as _impl
    result = _impl(usd_path)
    # Backward compat: old import_usd returned just a list
    if isinstance(result, tuple):
        return result
    return result, {}
