# pyright: reportArgumentType=none, reportCallIssue=none
"""scene/schema.py の MetricsConfig shape_metrics / shape_reference フィールド仕様ベーステスト．

仕様出典:
- docs/05_TECH/TECH_11_形状指標の見直しと新指標導入.md
- docs/04_SPEC/SPEC_02_シーン定義仕様.md（evaluation/metrics セクション）
- src/sm_tias_lightsim/scene/schema.py（MetricsConfig 定義）

テスト方針:
  - Pydantic モデルを直接構築して振る舞いを検証する（YAML パースは別途）
  - 既存フィールド（cov/std/max_min/radial_profile/radial_min_max）への非干渉を確認する
  - 後方互換性（既存 YAML が新スキーマで読める）を確認する
"""

from __future__ import annotations

import textwrap
from pathlib import Path

import pytest
import yaml

from sm_tias_lightsim.scene.schema import EvaluationConfig, MetricsConfig

# ===========================================================================
# F1. 後方互換: 既存 YAML（shape_metrics 未指定）が新スキーマで読める
# ===========================================================================


def test_existing_yaml_without_shape_metrics_is_valid(tmp_path: Path):
    """F1: shape_metrics を含まない既存 eval_baseline_dng_target.yaml 相当の YAML が
    新スキーマで ValidationError なしに読み込めること．

    仕様: shape_metrics および shape_reference フィールドはいずれも省略可能
    (TECH_11 §設定方法 後方互換)
    """
    yaml_content = textwrap.dedent("""\
        roi:
          type: image_pixel
          x: 24
          y: 37
          width: 464
          height: 450
        zones:
          enabled: true
          boundaries: [0.33, 0.66]
        metrics:
          cov: true
          std: true
          max_min: true
          radial_profile: true
          radial_min_max: true
        normalization:
          enabled: true
          type: mean_anchor
          target_mean: 207.82
    """)
    yaml_path = tmp_path / "eval_no_shape.yaml"
    yaml_path.write_text(yaml_content, encoding="utf-8")

    data = yaml.safe_load(yaml_path.read_text(encoding="utf-8"))
    config = EvaluationConfig.model_validate(data)

    # shape_metrics と shape_reference はデフォルト値（False / None）であること
    assert config.metrics is not None
    assert config.metrics.shape_metrics is False
    assert config.metrics.shape_reference is None


def test_metrics_config_without_shape_fields_defaults_to_false_and_none():
    """F1 補足: MetricsConfig を shape_metrics/shape_reference 未指定で構築すると
    shape_metrics=False, shape_reference=None になること（Pydantic デフォルト値）．

    仕様: shape_metrics: bool = Field(default=False, ...)
          shape_reference: str | None = Field(default=None, ...)
    (schema.py MetricsConfig)
    """
    cfg = MetricsConfig()
    assert cfg.shape_metrics is False
    assert cfg.shape_reference is None


def test_existing_config_files_are_backward_compatible():
    """F1 補足: プロジェクト既存の eval_baseline_dng_target.yaml が
    ValidationError なしに読み込めること（実ファイルでの後方互換確認）．

    仕様: 既存の YAML は変更不要で動作する (TECH_11 §後方互換)
    """
    import os
    project_root = Path(os.path.dirname(__file__)).parent.parent
    config_path = project_root / "configs" / "eval_baseline_dng_target.yaml"

    if not config_path.exists():
        pytest.skip("configs/eval_baseline_dng_target.yaml が存在しないためスキップ")

    with config_path.open(encoding="utf-8") as f:
        data = yaml.safe_load(f)

    # EvaluationConfig 直接または SceneConfig.evaluation サブツリーとして読む
    eval_data = data.get("evaluation", data)
    config = EvaluationConfig.model_validate(eval_data)
    # shape_metrics フィールドのデフォルトが有効であること
    assert config.metrics is not None
    assert config.metrics.shape_metrics is False


# ===========================================================================
# F2. shape_metrics=true + shape_reference=null が valid
# ===========================================================================


def test_shape_metrics_true_with_null_reference_is_valid():
    """F2: shape_metrics=True + shape_reference=None が valid であること．

    仕様: shape_metrics=True + shape_reference=null が valid
    (TECH_11 §設定方法 基本)
    """
    cfg = MetricsConfig(shape_metrics=True, shape_reference=None)
    assert cfg.shape_metrics is True
    assert cfg.shape_reference is None


def test_shape_metrics_true_null_reference_via_yaml(tmp_path: Path):
    """F2 補足: YAML で shape_metrics=true, shape_reference=null と指定した場合も valid．

    仕様: shape_metrics: true, shape_reference: null（YAML 表現）
    (configs/eval_baseline_dng_target_shape.yaml 相当)
    """
    yaml_content = textwrap.dedent("""\
        roi:
          type: image_pixel
          x: 0
          y: 0
          width: 100
          height: 100
        metrics:
          cov: true
          shape_metrics: true
          shape_reference: null
    """)
    data = yaml.safe_load(yaml_content)
    config = EvaluationConfig.model_validate(data)

    assert config.metrics is not None
    assert config.metrics.shape_metrics is True
    assert config.metrics.shape_reference is None


def test_shape_metrics_false_with_null_reference_is_valid():
    """F2 補足: shape_metrics=False + shape_reference=None（デフォルト）も valid．

    仕様: 省略時（null）は SSIM / NCC の列は空欄になる
    (schema.py MetricsConfig.shape_reference description)
    """
    cfg = MetricsConfig(shape_metrics=False, shape_reference=None)
    assert cfg.shape_metrics is False
    assert cfg.shape_reference is None


# ===========================================================================
# F3. shape_metrics=true + shape_reference="path/to/file.npy" が valid
# ===========================================================================


def test_shape_metrics_true_with_string_reference_is_valid():
    """F3: shape_metrics=True + shape_reference="path/to/file.npy" が valid であること．

    仕様: shape_metrics=True + shape_reference="path/to/file.npy" が valid
    (TECH_11 §設定方法 SSIM/NCC 有効化)
    """
    cfg = MetricsConfig(shape_metrics=True, shape_reference="path/to/reference.npy")
    assert cfg.shape_metrics is True
    assert cfg.shape_reference == "path/to/reference.npy"


def test_shape_metrics_true_with_reference_via_yaml(tmp_path: Path):
    """F3 補足: YAML で shape_reference をパス文字列で指定した場合も valid．

    仕様: shape_reference: reference_measurement/quantitative/expected_results/...npy
    (TECH_11 §設定方法 SSIM/NCC 有効化)
    """
    yaml_content = textwrap.dedent("""\
        roi:
          type: image_pixel
          x: 0
          y: 0
          width: 100
          height: 100
        metrics:
          shape_metrics: true
          shape_reference: "reference_measurement/quantitative/expected_results/sample.npy"
    """)
    data = yaml.safe_load(yaml_content)
    config = EvaluationConfig.model_validate(data)

    assert config.metrics is not None
    assert config.metrics.shape_metrics is True
    assert (
        config.metrics.shape_reference
        == "reference_measurement/quantitative/expected_results/sample.npy"
    )


def test_shape_reference_accepts_png_path():
    """F3 補足: shape_reference に .png パスも指定できること．

    仕様: shape_reference の .npy または .png パスを受け付ける
    (cli/evaluate.py _load_reference_luminance)
    """
    cfg = MetricsConfig(shape_metrics=True, shape_reference="path/to/reference.png")
    assert cfg.shape_reference == "path/to/reference.png"


# ===========================================================================
# 追加: 既存フィールドへの非干渉確認
# ===========================================================================


def test_existing_fields_unaffected_when_shape_metrics_added():
    """shape_metrics フィールド追加後も既存フィールドのデフォルト値が変わらないこと．

    仕様: 後方互換（既存の YAML は変更不要）
    (TECH_11 §設定方法 後方互換)
    """
    cfg = MetricsConfig()
    # 既存フィールドのデフォルト値が維持されている
    assert cfg.cov is True
    assert cfg.std is True
    assert cfg.max_min is True
    assert cfg.radial_profile is True
    assert cfg.radial_min_max is False
    # 新フィールドのデフォルト値
    assert cfg.shape_metrics is False
    assert cfg.shape_reference is None


def test_all_flags_can_be_enabled_simultaneously():
    """全フラグを True に指定できること（組み合わせ互換性の確認）．

    仕様: 各フラグは独立してオン/オフ可能
    """
    cfg = MetricsConfig(
        cov=True,
        std=True,
        max_min=True,
        radial_profile=True,
        radial_min_max=True,
        shape_metrics=True,
        shape_reference="path/to/ref.npy",
    )
    assert cfg.shape_metrics is True
    assert cfg.shape_reference == "path/to/ref.npy"
    assert cfg.radial_min_max is True


def test_shape_metrics_config_file_is_valid():
    """プロジェクト既存の eval_baseline_dng_target_shape.yaml が
    ValidationError なしに読み込めること．

    仕様: configs/eval_baseline_dng_target_shape.yaml が valid な設定ファイルとして
    機能すること (TECH_11 §設定方法)
    """
    import os
    project_root = Path(os.path.dirname(__file__)).parent.parent
    config_path = project_root / "configs" / "eval_baseline_dng_target_shape.yaml"

    if not config_path.exists():
        pytest.skip("configs/eval_baseline_dng_target_shape.yaml が存在しないためスキップ")

    with config_path.open(encoding="utf-8") as f:
        data = yaml.safe_load(f)

    eval_data = data.get("evaluation", data)
    config = EvaluationConfig.model_validate(eval_data)

    assert config.metrics is not None
    assert config.metrics.shape_metrics is True
