"""visualize テスト共通フィクスチャ．

照度ベイクテスト・ビューアテストで使用する SceneConfig と STL ファイルを提供する．
Mitsuba をモックせず実オブジェクトを使う（GUIDE_08 モック方針）．
"""

from __future__ import annotations

from pathlib import Path

import numpy as np
import pytest
import trimesh

from sm_tias_lightsim.scene.schema import SceneConfig


def _write_flat_plane_stl(path: Path, z_mm: float = 0.0) -> None:
    """z=z_mm の水平三角形 1 枚の STL を書き出す（mm 単位）．

    三角形の頂点:
        v0 = (0,  0, z_mm)
        v1 = (10, 0, z_mm)
        v2 = (10, 10, z_mm)
    法線は +Z 方向（仕様に合わせ正規化は trimesh に任せる）．
    """
    vertices = np.array(
        [
            [0.0, 0.0, z_mm],
            [10.0, 0.0, z_mm],
            [10.0, 10.0, z_mm],
        ],
        dtype=np.float64,
    )
    faces = np.array([[0, 1, 2]], dtype=np.int64)
    mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
    mesh.export(str(path))


def _write_blocker_stl(path: Path) -> None:
    """LED と評価板の間に配置する遮蔽板 STL を書き出す（mm 単位）．

    z=50mm に大きな平面を置く（光源 z=100mm, 評価板 z=0mm の中間）．
    頂点: (−100, −100, 50), (100, −100, 50), (100, 100, 50), (−100, 100, 50)
    法線は +Z 方向．
    """
    vertices = np.array(
        [
            [-100.0, -100.0, 50.0],
            [100.0, -100.0, 50.0],
            [100.0, 100.0, 50.0],
            [-100.0, 100.0, 50.0],
        ],
        dtype=np.float64,
    )
    # 2 三角形で四角形を構成
    faces = np.array([[0, 1, 2], [0, 2, 3]], dtype=np.int64)
    mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
    mesh.export(str(path))


def _write_downward_plane_stl(path: Path, z_mm: float = 0.0) -> None:
    """法線が -Z 方向の水平三角形 1 枚の STL を書き出す（mm 単位）．

    頂点の巻き順を逆にして -Z 法線を作る．
    """
    vertices = np.array(
        [
            [0.0, 0.0, z_mm],
            [10.0, 10.0, z_mm],
            [10.0, 0.0, z_mm],
        ],
        dtype=np.float64,
    )
    faces = np.array([[0, 1, 2]], dtype=np.int64)
    mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
    mesh.export(str(path))


def _make_minimal_scene_config(
    mesh_path: str,
    mesh_id: str = "plane",
    light_dict: dict | None = None,
) -> SceneConfig:
    """最小限の SceneConfig を辞書から作成する．

    Args:
        mesh_path: STL ファイルへのパス文字列（プロジェクトルート相対 or 絶対）．
        mesh_id: メッシュ ID．
        light_dict: lights リストの要素辞書 1 個．None なら PointLight のデフォルトを使う．
    """
    if light_dict is None:
        light_dict = {
            "id": "led",
            "type": "point",
            "position": [0.0, 0.0, 100.0],
            "intensity": 1.0,
            "profile": "lambertian",
        }

    data = {
        "name": "test_bake",
        "geometry": {
            "meshes": [
                {
                    "id": mesh_id,
                    "path": mesh_path,
                    "unit": "mm",
                    "material": "white",
                }
            ]
        },
        "materials": {
            "white": {"type": "diffuse", "reflectance": 0.9},
        },
        "lights": [light_dict],
        "sensor": {
            "type": "perspective",
            "position": [0.0, 0.0, 200.0],
            "target": [0.0, 0.0, 0.0],
            "up": [0.0, 1.0, 0.0],
            "fov_deg": 60.0,
            "film": {"width": 64, "height": 64},
        },
        "render": {
            "variant": "scalar_rgb",
            "integrator": "path",
            "max_depth": 2,
            "spp": 1,
        },
    }
    return SceneConfig.model_validate(data)


@pytest.fixture
def flat_plane_stl(tmp_path: Path) -> Path:
    """法線 +Z の水平三角形 1 枚の STL を tmp_path に書き出す（z=0mm）."""
    path = tmp_path / "flat_plane.stl"
    _write_flat_plane_stl(path, z_mm=0.0)
    return path


@pytest.fixture
def blocker_stl(tmp_path: Path) -> Path:
    """LED (z=100mm) と評価板 (z=0mm) の間（z=50mm）に置く遮蔽板 STL."""
    path = tmp_path / "blocker.stl"
    _write_blocker_stl(path)
    return path


@pytest.fixture
def downward_plane_stl(tmp_path: Path) -> Path:
    """法線 -Z の水平三角形 1 枚の STL を tmp_path に書き出す（z=0mm）."""
    path = tmp_path / "downward_plane.stl"
    _write_downward_plane_stl(path, z_mm=0.0)
    return path


@pytest.fixture
def point_light_scene_config(flat_plane_stl: Path) -> tuple[SceneConfig, Path]:
    """PointLight + 法線 +Z 平面の SceneConfig と project_root を返す．

    光源: position=(0, 0, 100mm), intensity=1.0
    メッシュ: z=0mm 三角形（法線 +Z）
    """
    config = _make_minimal_scene_config(
        mesh_path=str(flat_plane_stl),
        mesh_id="plane",
        light_dict={
            "id": "led",
            "type": "point",
            "position": [0.0, 0.0, 100.0],
            "intensity": 1.0,
            "profile": "lambertian",
        },
    )
    return config, flat_plane_stl.parent


@pytest.fixture
def directional_light_scene_config(flat_plane_stl: Path) -> tuple[SceneConfig, Path]:
    """DirectionalLight + 法線 +Z 平面の SceneConfig と project_root を返す．

    光源: direction=(0, 0, -1)（下向き）, irradiance=1.0
    メッシュ: z=0mm 三角形（法線 +Z）
    """
    config = _make_minimal_scene_config(
        mesh_path=str(flat_plane_stl),
        mesh_id="plane",
        light_dict={
            "id": "dir_light",
            "type": "directional",
            "direction": [0.0, 0.0, -1.0],
            "irradiance": 1.0,
        },
    )
    return config, flat_plane_stl.parent
