# pyright: reportCallIssue=none
# Mitsuba 3 は variant ベースの動的属性解決を行うため Pylance/Pyright が
# mi.* を object 型と推論し reportCallIssue を誤検知する（coding-style.md の方針）．
"""Mitsuba レンダリング実行モジュール．

variant 設定と mi.render() の実行を担う．
`mitsuba.set_variant` はこのモジュールで 1 度だけ呼ぶ．
"""

from __future__ import annotations

import logging
import shutil
from pathlib import Path

import numpy

from sm_tias_lightsim.runtime.llvm_init import ensure_drjit_libllvm_path, log_variant_status
from sm_tias_lightsim.scene.builder import build_scene
from sm_tias_lightsim.scene.schema import SceneConfig

_logger = logging.getLogger(__name__)

# 現在設定されている variant を追跡する（プロセス内 1 度だけ変更可能）
_current_variant: str | None = None


def render_scene(config: SceneConfig, project_root: Path | None = None) -> numpy.ndarray:
    """SceneConfig からシーンを構築してレンダリングし，HDR 画像（H, W, 3）を返す．

    Mitsuba の variant を config.render.variant に設定する（プロセス内初回のみ有効）．
    既に異なる variant が設定されている場合は警告ログを出して続行する．

    llvm_* variant 要求時は mi.set_variant 前に DRJIT_LIBLLVM_PATH を自動探索して設定する．
    mitsuba は関数内でインポートするため，モジュール import 時点では LLVM 初期化が走らない．
    set_variant 成功後に variant 状態を INFO ログで出力する．

    Args:
        config: 検証済み SceneConfig．
        project_root: STL 相対パスの基点ディレクトリ．None なら CWD を使用．

    Returns:
        HDR 画像 (H, W, 3) の float32 配列．値域は物理量（0〜∞ も許容）．
    """
    global _current_variant  # noqa: PLW0603

    desired_variant = config.render.variant

    if _current_variant is None:
        # llvm_* variant の場合のみ DRJIT_LIBLLVM_PATH を自動探索する
        # scalar_* / cuda_* は LLVM-C.dll 不要なので探索しない
        # mitsuba の import 前に os.environ['DRJIT_LIBLLVM_PATH'] を設定することが必須．
        # mitsuba を先に import すると LLVM 初期化が走り，LLVM-C.dll 未設定で ImportError になる．
        if desired_variant.startswith("llvm_"):
            ensure_drjit_libllvm_path()

    # mitsuba は DRJIT_LIBLLVM_PATH 設定後にインポートする．
    # モジュールレベルで import すると，ensure_drjit_libllvm_path() の前に LLVM 初期化が走り
    # ImportError になるため，この位置に遅延 import する．
    import mitsuba as mi

    if _current_variant is None:
        try:
            mi.set_variant(desired_variant)
        except ImportError as e:
            if desired_variant.startswith("llvm_") and "LLVM" in str(e):
                _logger.error(
                    "Mitsuba variant=%s の JIT 初期化に失敗しました．"
                    "LLVM-C.dll が見つかりません．"
                    "Windows: C:\\Program Files\\LLVM\\bin\\LLVM-C.dll を配置するか，"
                    "DRJIT_LIBLLVM_PATH 環境変数を設定してください．"
                    "詳細は docs/02_ENV/ENV_02_環境構築手順.md を参照．",
                    desired_variant,
                )
            raise

        _current_variant = desired_variant
        log_variant_status(_logger, desired_variant, mi.variant())
    elif _current_variant != desired_variant:
        _logger.warning(
            "Mitsuba variant is already set to %r; "
            "requested variant %r will be ignored. "
            "Restart the process to change the variant.",
            _current_variant,
            desired_variant,
        )
    # 同じ variant が既に設定されている場合は何もしない

    scene_dict, tempdir = build_scene(config, project_root=project_root)
    try:
        scene = mi.load_dict(scene_dict)
        image_tensor = mi.render(scene, spp=config.render.spp)
        arr = numpy.array(image_tensor)
    finally:
        shutil.rmtree(tempdir, ignore_errors=True)

    return arr
