"""
Pure functions to build renderable geometry from domain objects.
No UI imports - returns raw numpy arrays and connectivity data.
"""

from typing import Any

import numpy as np
from caliscope.cameras.camera_array import CameraArray


def build_camera_geometry(camera_array: CameraArray, scale: float = 0.0005) -> dict[str, Any] | None:
    """
    Build static camera mesh geometry as pyramid frustums.
    Corrects the rotation math and aligns with OpenCV Y-down convention.

    Args:
        camera_array: The camera array with extrinsic calibration.
        scale: Size multiplier for camera frustums. Default 0.0005 works well
               for calibration views. Use smaller values (e.g., 0.0002) when
               cameras dominate the scene relative to tracked points.
    """
    if not camera_array.all_extrinsics_calibrated():
        return None

    all_vertices = []
    all_triangles = []
    all_edges = []
    all_colors = []
    labels = []
    vertex_offset = 0

    # Color scheme: green for cameras
    cam_color = np.array([0.2, 0.8, 0.2], dtype=np.float32)

    for cam_id, cam in camera_array.cameras.items():
        if cam.rotation is None or cam.translation is None or cam.matrix is None:
            continue

        # 1. Camera Intrinsics
        fx, fy = cam.matrix[0, 0], cam.matrix[1, 1]
        cx, cy = cam.matrix[0, 2], cam.matrix[1, 2]
        w, h = cam.size

        # Scale to reasonable size (normalize by focal length)
        f_avg = (fx + fy) / 2 * scale

        # 2. Local Vertices (OpenCV convention: X-right, Y-down, Z-forward)
        # Apex is origin. Image corners at z = f_avg.
        # Top-Left (u=0, v=0) -> x = -cx, y = -cy
        # Bottom-Right (u=w, v=h) -> x = w-cx, y = h-cy
        verts_local = np.array(
            [
                [0, 0, 0],  # 0: Apex (Camera Center)
                [-cx * scale, -cy * scale, f_avg],  # 1: Top-Left
                [(w - cx) * scale, -cy * scale, f_avg],  # 2: Top-Right
                [(w - cx) * scale, (h - cy) * scale, f_avg],  # 3: Bottom-Right
                [-cx * scale, (h - cy) * scale, f_avg],  # 4: Bottom-Left
            ],
            dtype=np.float32,
        )

        # 3. Transform to world space
        # R_world_to_cam = cam.rotation
        # R_cam_to_world = cam.rotation.T
        # T_world = -R_cam_to_world @ cam.translation
        # Math: v_world = (R_cam_to_world @ v_local) + T_world
        # Row math: v_world = (v_local @ R_cam_to_world.T) + T_world
        # v_world = (v_local @ cam.rotation) + T_world

        R_cam_to_world = cam.rotation.T
        t_world = -R_cam_to_world @ cam.translation

        verts_world = (verts_local @ cam.rotation) + t_world

        # 4. Define triangles (standard pyramid topology, base quad split into 2 triangles)
        o = vertex_offset
        triangles = [
            [o + 0, o + 1, o + 2],  # Side: Apex to Top
            [o + 0, o + 2, o + 3],  # Side: Apex to Right
            [o + 0, o + 3, o + 4],  # Side: Apex to Bottom
            [o + 0, o + 4, o + 1],  # Side: Apex to Left
            [o + 1, o + 2, o + 3],  # Base: first triangle
            [o + 1, o + 3, o + 4],  # Base: second triangle
        ]
        all_triangles.extend(triangles)

        # 5. Define edges for wireframe rendering
        edges = [
            [o + 0, o + 1],  # Apex to Top-Left
            [o + 0, o + 2],  # Apex to Top-Right
            [o + 0, o + 3],  # Apex to Bottom-Right
            [o + 0, o + 4],  # Apex to Bottom-Left
            [o + 1, o + 2],  # Base: Top edge
            [o + 2, o + 3],  # Base: Right edge
            [o + 3, o + 4],  # Base: Bottom edge
            [o + 4, o + 1],  # Base: Left edge
        ]
        all_edges.extend(edges)

        all_vertices.append(verts_world)
        all_colors.append(np.tile(cam_color, (len(verts_world), 1)))

        # Add label position (slightly above the apex)
        label_pos = t_world + np.array([0, 0, 0.1], dtype=np.float32)
        labels.append((label_pos, f"Cam {cam_id}"))

        vertex_offset += len(verts_world)

    if not all_vertices:
        return None

    return {
        "vertices": np.concatenate(all_vertices),
        "triangles": np.array(all_triangles, dtype=np.int32),
        "edges": np.array(all_edges, dtype=np.int32),
        "colors": np.concatenate(all_colors),
        "labels": labels,
    }
