"""plotter.py の単体テスト.

仕様 (SPEC_01_アーキテクチャ設計.md):
  - plot_luminance_map: ヒートマップを PNG 保存
  - plot_histogram: ヒストグラムを PNG 保存
  - plot_radial_profile: 放射状プロファイルを折れ線グラフで PNG 保存
  - plot_zone_map: ゾーンオーバーレイ輝度マップを PNG 保存

テスト方針 (GUIDE_08_テスト方針.md):
  - 出力ファイルが生成されることを確認する程度でよい
  - tmp_path フィクスチャを使用
"""

import matplotlib
matplotlib.use("Agg")  # GUI なし環境でも動作するバックエンドを設定

from pathlib import Path

import numpy
import pytest

from src.visualization.plotter import (
    plot_histogram,
    plot_luminance_map,
    plot_radial_profile,
    plot_zone_map,
)


@pytest.fixture
def sample_luminance() -> numpy.ndarray:
    """テスト用の 8x8 輝度画像（float64）を返す fixture."""
    return numpy.random.uniform(50.0, 200.0, (8, 8)).astype(numpy.float64)


# ---------------------------------------------------------------------------
# plot_luminance_map テスト
# ---------------------------------------------------------------------------


class TestPlotLuminanceMap:
    """plot_luminance_map 関数のテスト群."""

    def test_output_file_is_created(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが生成されること."""
        output_path = str(tmp_path / "luminance_map.png")
        plot_luminance_map(sample_luminance, output_path)
        assert Path(output_path).exists()

    def test_output_file_is_not_empty(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが空でないこと（PNG バイナリが書き込まれていること）."""
        output_path = str(tmp_path / "luminance_map.png")
        plot_luminance_map(sample_luminance, output_path)
        assert Path(output_path).stat().st_size > 0

    def test_creates_parent_directory_automatically(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ディレクトリが存在しない場合に自動作成されること."""
        nested_path = str(tmp_path / "figures" / "sub" / "luminance_map.png")
        plot_luminance_map(sample_luminance, nested_path)
        assert Path(nested_path).exists()

    def test_uniform_luminance_is_saved(self, tmp_path: Path) -> None:
        """正常: 均一輝度画像でも正常に保存されること."""
        uniform_luminance = numpy.full((8, 8), 128.0, dtype=numpy.float64)
        output_path = str(tmp_path / "uniform_map.png")
        plot_luminance_map(uniform_luminance, output_path)
        assert Path(output_path).exists()


# ---------------------------------------------------------------------------
# plot_histogram テスト
# ---------------------------------------------------------------------------


class TestPlotHistogram:
    """plot_histogram 関数のテスト群."""

    def test_output_file_is_created(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが生成されること."""
        output_path = str(tmp_path / "histogram.png")
        plot_histogram(sample_luminance, output_path)
        assert Path(output_path).exists()

    def test_output_file_is_not_empty(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが空でないこと."""
        output_path = str(tmp_path / "histogram.png")
        plot_histogram(sample_luminance, output_path)
        assert Path(output_path).stat().st_size > 0

    def test_creates_parent_directory_automatically(
        self, sample_luminance: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ディレクトリが存在しない場合に自動作成されること."""
        nested_path = str(tmp_path / "figures" / "sub" / "histogram.png")
        plot_histogram(sample_luminance, nested_path)
        assert Path(nested_path).exists()

    def test_uniform_luminance_is_saved(self, tmp_path: Path) -> None:
        """正常: 均一輝度画像でも正常に保存されること."""
        uniform_luminance = numpy.full((8, 8), 200.0, dtype=numpy.float64)
        output_path = str(tmp_path / "uniform_histogram.png")
        plot_histogram(uniform_luminance, output_path)
        assert Path(output_path).exists()


# ---------------------------------------------------------------------------
# plot_radial_profile テスト
# ---------------------------------------------------------------------------


class TestPlotRadialProfile:
    """plot_radial_profile 関数のテスト群."""

    @pytest.fixture
    def sample_radial_profile(self) -> numpy.ndarray:
        """テスト用の放射状プロファイル（20 x 2）を返す fixture."""
        bins = numpy.linspace(0.025, 0.975, 20)
        luminance = numpy.linspace(200.0, 150.0, 20)
        return numpy.column_stack([bins, luminance])

    def test_output_file_is_created(
        self, sample_radial_profile: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが生成されること."""
        output_path = str(tmp_path / "radial_profile.png")
        plot_radial_profile(sample_radial_profile, output_path)
        assert Path(output_path).exists()

    def test_output_file_is_not_empty(
        self, sample_radial_profile: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ファイルが空でないこと（PNG バイナリが書き込まれていること）."""
        output_path = str(tmp_path / "radial_profile.png")
        plot_radial_profile(sample_radial_profile, output_path)
        assert Path(output_path).stat().st_size > 0

    def test_creates_parent_directory_automatically(
        self, sample_radial_profile: numpy.ndarray, tmp_path: Path
    ) -> None:
        """正常: 出力ディレクトリが存在しない場合に自動作成されること."""
        nested_path = str(tmp_path / "figures" / "sub" / "radial_profile.png")
        plot_radial_profile(sample_radial_profile, nested_path)
        assert Path(nested_path).exists()

    def test_uniform_profile_is_saved(self, tmp_path: Path) -> None:
        """正常: 均一輝度プロファイルでも正常に保存されること."""
        bins = numpy.linspace(0.025, 0.975, 20)
        luminance = numpy.full(20, 200.0)
        profile = numpy.column_stack([bins, luminance])
        output_path = str(tmp_path / "uniform_radial.png")
        plot_radial_profile(profile, output_path)
        assert Path(output_path).exists()


# ---------------------------------------------------------------------------
# plot_zone_map テスト
# ---------------------------------------------------------------------------


class TestPlotZoneMap:
    """plot_zone_map 関数のテスト群."""

    @pytest.fixture
    def sample_inputs(self) -> tuple[numpy.ndarray, numpy.ndarray]:
        """テスト用の輝度画像・ゾーンマップを返す fixture."""
        luminance = numpy.random.uniform(100.0, 200.0, (20, 20)).astype(numpy.float64)
        zone_map = numpy.full((20, 20), 2, dtype=numpy.uint8)
        zone_map[4:16, 4:16] = 1
        zone_map[7:13, 7:13] = 0
        return luminance, zone_map

    def test_output_file_is_created(
        self,
        sample_inputs: tuple[numpy.ndarray, numpy.ndarray],
        tmp_path: Path,
    ) -> None:
        """正常: 出力ファイルが生成されること."""
        luminance, zone_map = sample_inputs
        output_path = str(tmp_path / "zone_map.png")
        plot_zone_map(luminance, zone_map, output_path)
        assert Path(output_path).exists()

    def test_output_file_is_not_empty(
        self,
        sample_inputs: tuple[numpy.ndarray, numpy.ndarray],
        tmp_path: Path,
    ) -> None:
        """正常: 出力ファイルが空でないこと（PNG バイナリが書き込まれていること）."""
        luminance, zone_map = sample_inputs
        output_path = str(tmp_path / "zone_map.png")
        plot_zone_map(luminance, zone_map, output_path)
        assert Path(output_path).stat().st_size > 0

    def test_creates_parent_directory_automatically(
        self,
        sample_inputs: tuple[numpy.ndarray, numpy.ndarray],
        tmp_path: Path,
    ) -> None:
        """正常: 出力ディレクトリが存在しない場合に自動作成されること."""
        luminance, zone_map = sample_inputs
        nested_path = str(tmp_path / "figures" / "sub" / "zone_map.png")
        plot_zone_map(luminance, zone_map, nested_path)
        assert Path(nested_path).exists()
