"""scene/schema.py の load_scene_yaml 関数の仕様ベーステスト．

仕様出典: docs/04_SPEC/SPEC_02_シーン定義仕様.md
モック方針: ファイル I/O はモックせず tmp_path フィクスチャで実ファイルを使う (testing.md)
"""

from pathlib import Path

import pytest
import yaml
from pydantic import ValidationError

from sm_tias_lightsim.scene import SceneConfig, load_scene_yaml


@pytest.fixture
def valid_yaml_content() -> str:
    """load_scene_yaml テスト用の有効な YAML 文字列．"""
    return """\
name: loader_test_scene
geometry:
  meshes:
    - id: housing
      path: configs/meshes/housing.stl
      unit: mm
      material: mat_diffuse
materials:
  mat_diffuse:
    type: diffuse
    reflectance: 0.5
lights:
  - id: led_top
    type: point
    position: [0.0, 0.0, 50.0]
    intensity: [1.0, 1.0, 1.0]
    profile: lambertian
sensor:
  type: perspective
  position: [0.0, 0.0, 100.0]
  target: [0.0, 0.0, 0.0]
  up: [0.0, 1.0, 0.0]
  fov_deg: 60.0
  film:
    width: 1920
    height: 1080
render:
  variant: llvm_ad_rgb
  integrator: path
  max_depth: 8
  spp: 256
"""


def test_load_scene_yaml_returns_scene_config(tmp_path, valid_yaml_content):
    """正常な YAML ファイルから SceneConfig インスタンスが返ること．

    仕様: load_scene_yaml(path) -> SceneConfig (SPEC_02 バリデーション節)
    """
    yaml_file = tmp_path / "scene.yaml"
    yaml_file.write_text(valid_yaml_content, encoding="utf-8")

    config = load_scene_yaml(yaml_file)

    assert isinstance(config, SceneConfig)
    assert config.name == "loader_test_scene"


def test_load_scene_yaml_raises_file_not_found(tmp_path):
    """存在しないパスを渡したとき FileNotFoundError が発生すること．

    仕様: FileNotFoundError: ファイルが存在しない場合 (schema.py docstring)
    """
    nonexistent = tmp_path / "does_not_exist.yaml"

    with pytest.raises(FileNotFoundError):
        load_scene_yaml(nonexistent)


def test_load_scene_yaml_raises_yaml_error(tmp_path):
    """不正な YAML 構文のファイルを渡したとき yaml.YAMLError 系の例外が発生すること．

    仕様: yaml.YAMLError: YAML 構文エラー (schema.py docstring)
    """
    invalid_yaml = tmp_path / "invalid.yaml"
    # タブ文字を含む不正な YAML
    invalid_yaml.write_text("name: test\n\t invalid_indent: [unclosed", encoding="utf-8")

    with pytest.raises(yaml.YAMLError):
        load_scene_yaml(invalid_yaml)


def test_load_scene_yaml_raises_validation_error(tmp_path):
    """構文は正しいが必須キーが欠落した YAML で ValidationError が発生すること．

    仕様: pydantic.ValidationError: スキーマ違反 (schema.py docstring)
    """
    # name が欠落した構文的に正しい YAML
    missing_name_yaml = """\
geometry:
  meshes:
    - id: housing
      path: configs/meshes/housing.stl
      unit: mm
      material: mat_diffuse
materials:
  mat_diffuse:
    type: diffuse
    reflectance: 0.5
lights:
  - id: led_top
    type: point
    position: [0.0, 0.0, 50.0]
    intensity: 1.0
    profile: lambertian
sensor:
  type: perspective
  position: [0.0, 0.0, 100.0]
  target: [0.0, 0.0, 0.0]
  up: [0.0, 1.0, 0.0]
  fov_deg: 60.0
  film:
    width: 1920
    height: 1080
"""
    yaml_file = tmp_path / "missing_name.yaml"
    yaml_file.write_text(missing_name_yaml, encoding="utf-8")

    with pytest.raises(ValidationError):
        load_scene_yaml(yaml_file)


def test_load_scene_yaml_accepts_pathlib_path(tmp_path, valid_yaml_content):
    """Path オブジェクトを引数として受け付けること．

    仕様: load_scene_yaml(path: str | Path) (schema.py シグネチャ)
    """
    yaml_file = tmp_path / "scene.yaml"
    yaml_file.write_text(valid_yaml_content, encoding="utf-8")

    # Path オブジェクトを渡す
    config = load_scene_yaml(Path(yaml_file))

    assert isinstance(config, SceneConfig)


def test_load_scene_yaml_accepts_str_path(tmp_path, valid_yaml_content):
    """文字列パスを引数として受け付けること．

    仕様: load_scene_yaml(path: str | Path) (schema.py シグネチャ)
    """
    yaml_file = tmp_path / "scene.yaml"
    yaml_file.write_text(valid_yaml_content, encoding="utf-8")

    # 文字列パスを渡す
    config = load_scene_yaml(str(yaml_file))

    assert isinstance(config, SceneConfig)
