#!/usr/bin/env python3
"""統合版 hierarchical_config.py テストケース
test_hierarchical_config.py, test_hierarchical_config_fixed.py, test_hierarchical_config_public.py を統合


仕様書: SPEC-INFRASTRUCTURE
"""

import os
import sys
from pathlib import Path
from unittest.mock import patch

import pytest
import yaml

# テスト対象をインポート
# PathManager統一パス管理システムを使用(事前にパスを設定)
sys.path.insert(0, str(Path(__file__).parent.parent))  # scripts/を追加
from noveler.infrastructure.utils.path_manager import ensure_imports

ensure_imports()
from noveler.infrastructure.adapters.hierarchical_config_adapter import HierarchicalConfigAdapter


class TestHierarchicalConfigCore:
    """HierarchicalConfigAdapterの基本機能テスト"""

    @pytest.fixture
    def temp_dir(self, tmp_path):
        """一時ディレクトリの作成"""
        return tmp_path

    @pytest.fixture
    def config_with_project(self, temp_dir: object):
        """プロジェクトルートありの設定"""
        project_root = temp_dir / "test_project"
        project_root.mkdir()

        # プロジェクト設定ファイルを作成
        config_file = project_root / "プロジェクト設定.yaml"
        config_data = {
            "project": {
                "name": "テストプロジェクト",
                "version": "1.0.0",
            },
        }
        with Path(config_file).open("w", encoding="utf-8") as f:
            yaml.dump(config_data, f, allow_unicode=True)

        return HierarchicalConfigAdapter(project_root)

    @pytest.fixture
    def config_without_project(self):
        """プロジェクトルートなしの設定"""
        return HierarchicalConfigAdapter(None)

    def test_init_with_project_root(self, temp_dir) -> None:
        """プロジェクトルートありでの初期化"""
        config = HierarchicalConfigAdapter(temp_dir)
        assert config.project_root == temp_dir

    def test_init_without_project_root(self) -> None:
        """プロジェクトルートなしでの初期化"""
        config = HierarchicalConfigAdapter()
        assert config.project_root is None

    def test_find_project_root_found(self, temp_dir) -> None:
        """プロジェクトルートの検出(見つかる場合)"""
        # プロジェクト設定ファイルを作成
        config_file = temp_dir / "プロジェクト設定.yaml"
        config_file.touch()

        # サブディレクトリから検索
        sub_dir = temp_dir / "sub" / "dir"
        sub_dir.mkdir(parents=True)

        with patch("pathlib.Path.cwd", return_value=sub_dir):
            config = HierarchicalConfigAdapter()
            assert config.project_root == temp_dir

    def test_find_project_root_not_found(self, temp_dir) -> None:
        """プロジェクトルートの検出(見つからない場合)"""
        with patch("pathlib.Path.cwd", return_value=temp_dir):
            config = HierarchicalConfigAdapter()
            assert config.project_root is None

    def test_get_default_config(self, config_without_project) -> None:
        """デフォルト設定の取得"""
        all_config = config_without_project.all()
        # デフォルト設定がマージされた設定に含まれることを確認
        assert "quality_management" in all_config
        assert "default_threshold" in all_config["quality_management"]
        assert all_config["quality_management"]["default_threshold"] == 80

    def test_load_env_config(self) -> None:
        """環境変数からの設定読み込み"""
        with patch.dict(os.environ, {"NOVEL_QUALITY_THRESHOLD": "80", "OTHER_VAR": "value"}):
            # 環境変数を再読み込みするために新しいインスタンスを作成
            config = HierarchicalConfigAdapter()
            all_config = config.all()
            # 環境変数の設定が反映されていることを確認
            # NOVEL_QUALITY_THRESHOLD -> quality.threshold
            assert "quality" in all_config
            assert all_config["quality"]["threshold"] == 80

    def test_get_all_config(self, config_with_project) -> None:
        """全設定の取得"""
        all_config = config_with_project.all()
        assert isinstance(all_config, dict)
        assert "project" in all_config
        assert all_config["project"]["name"] == "テストプロジェクト"

    def test_get_specific_key(self, config_with_project) -> None:
        """特定キーの取得"""
        # 存在するキー
        value = config_with_project.get("project.name")
        assert value == "テストプロジェクト"

        # 存在しないキー(デフォルト値なし)
        value = config_with_project.get("nonexistent.key")
        assert value is None

        # 存在しないキー(デフォルト値あり)
        value = config_with_project.get("nonexistent.key", "default")
        assert value == "default"

    def test_set_and_save(self, temp_dir) -> None:
        """設定の変更と保存"""
        config = HierarchicalConfigAdapter(temp_dir)

        # プロジェクト設定を変更
        config.set("test.value", 42, level="project")
        config.save("project")

        # 設定ファイルが作成されたことを確認
        config_file = temp_dir / "プロジェクト設定.yaml"
        assert config_file.exists()

        # 内容を確認
        with Path(config_file).open(encoding="utf-8") as f:
            saved_data = yaml.safe_load(f)
        assert saved_data["test"]["value"] == 42

    def test_merge_configs_behavior(self, tmp_path) -> None:
        """設定のマージ動作"""
        # プロジェクト設定を作成
        config_file = tmp_path / "プロジェクト設定.yaml"
        project_config = {"a": 1, "b": {"c": 2}}
        with Path(config_file).open("w", encoding="utf-8") as f:
            yaml.dump(project_config, f, allow_unicode=True)

        # 環境変数を設定してマージ動作を確認
        with patch.dict(os.environ, {"NOVEL_B_D": "3", "NOVEL_E": "4"}):
            config = HierarchicalConfigAdapter(tmp_path)
            merged = config.all()

            # プロジェクト設定が含まれる
            assert merged["a"] == 1
            assert merged["b"]["c"] == 2

            # 環境変数からの設定がマージされる
            # NOVEL_B_D -> b.d
            assert "b" in merged
            assert merged["b"].get("d") == 3
            # NOVEL_E -> e
            assert merged.get("e") == 4

    def test_type_conversion(self) -> None:
        """型変換のテスト"""
        with patch.dict(
            os.environ,
            {
                "NOVEL_QUALITY_THRESHOLD": "85",
                "NOVEL_DEBUG": "true",
                "NOVEL_MAX_RETRIES": "3",
            },
        ):
            config = HierarchicalConfigAdapter()
            config.all()

            # 環境変数経由の値が適切に変換されることを確認
            # NOVEL_QUALITY_THRESHOLD -> quality.threshold (数値に変換)
            assert config.get("quality.threshold") == 85
            # NOVEL_DEBUG -> debug (ブール値に変換)
            assert config.get("debug") is True
            # NOVEL_MAX_RETRIES -> max.retries (数値に変換)
            assert config.get("max.retries") == 3


class TestHierarchicalConfigPublicAPI:
    """公開APIのテスト"""

    @pytest.fixture
    def temp_project(self, tmp_path):
        """テスト用プロジェクトの作成"""
        project_dir = tmp_path / "test_project"
        project_dir.mkdir()

        # プロジェクト設定
        config_file = project_dir / "プロジェクト設定.yaml"
        config_data = {
            "project": {
                "title": "テスト小説",
                "author": "太郎",
            },
            "quality": {
                "threshold": 75,
            },
        }
        with Path(config_file).open("w", encoding="utf-8") as f:
            yaml.dump(config_data, f, allow_unicode=True)

        return project_dir

    def test_basic_usage(self, temp_project) -> None:
        """基本的な使用方法"""
        config = HierarchicalConfigAdapter(temp_project)

        # getメソッド
        assert config.get("project.title") == "テスト小説"
        assert config.get("project.author") == "太郎"
        assert config.get("quality.threshold") == 75

    def test_environment_override(self, temp_project) -> None:
        """環境変数による上書き"""
        with patch.dict(os.environ, {"NOVEL_QUALITY_THRESHOLD": "90"}):
            config = HierarchicalConfigAdapter(temp_project)

            # 環境変数が優先される (NOVEL_QUALITY_THRESHOLD -> quality.threshold)
            assert config.get("quality.threshold") == 90
            # プロジェクト設定も残っている(但し環境変数で上書きされる)
            # プロジェクト設定のみの場合のテスト
            with patch.dict(os.environ, {}, clear=True):
                config2 = HierarchicalConfigAdapter(temp_project)
                assert config2.get("quality.threshold") == 75

    def test_nested_get_with_dot_notation(self, temp_project) -> None:
        """ドット記法によるネストされた値の取得"""
        config = HierarchicalConfigAdapter(temp_project)

        # 深いネストのテスト
        config.set("deep.nested.value", "test", level="project")
        assert config.get("deep.nested.value") == "test"

    def test_error_handling(self) -> None:
        """エラーハンドリング"""
        config = HierarchicalConfigAdapter()

        # 無効なレベルでの保存
        with pytest.raises(ValueError, match=".*"):
            config.set("test", "value", level="invalid")
            config.save("invalid")


class TestHierarchicalConfigAdvanced:
    """高度な機能のテスト"""

    def test_init_global_config(self, tmp_path, monkeypatch) -> None:
        """グローバル設定の初期化"""
        # ホームディレクトリをテンポラリに変更
        home_dir = tmp_path / "home"
        home_dir.mkdir()
        monkeypatch.setattr(Path, "home", lambda: home_dir)

        config = HierarchicalConfigAdapter()

        # 設定が正常に作成されることを確認(ディレクトリ作成は必須ではない)
        assert config is not None
        assert config.all() is not None

    def test_show_config_sources(self, capsys) -> None:
        """設定ソースの表示"""
        config = HierarchicalConfigAdapter()
        # show_sourcesメソッドが存在しない場合は、get_allで代替
        if hasattr(config, "show_sources"):
            config.show_sources()
            captured = capsys.readouterr()
            assert "設定ソース" in captured.out or "プロジェクト設定" in captured.out
        else:
            # show_sourcesメソッドがない場合は基本機能をテスト
            all_config = config.all()
            assert isinstance(all_config, dict)
            # デフォルト設定が含まれることを確認
            assert "quality_management" in all_config or "default_project" in all_config

    def test_empty_configs_handling(self) -> None:
        """空の設定の処理"""
        config = HierarchicalConfigAdapter()

        # 空の設定でもエラーにならない
        assert config.get("nonexistent") is None
        assert config.all() is not None

    def test_deep_nested_config(self) -> None:
        """深くネストされた設定"""
        config = HierarchicalConfigAdapter()

        # setメソッドが存在しない場合は、getの基本機能をテスト
        if hasattr(config, "set") and hasattr(config, "save"):
            # 深いネストの設定
            config.set("level1.level2.level3.level4", "deep_value", level="project")

            # 取得できることを確認
            assert config.get("level1.level2.level3.level4") == "deep_value"

            # 中間レベルも辞書として取得可能
            level2 = config.get("level1.level2")
            assert isinstance(level2, dict)
            assert level2["level3"]["level4"] == "deep_value"
        else:
            # setメソッドがない場合は基本機能をテスト
            all_config = config.all()
            assert isinstance(all_config, dict)
            # デフォルト設定が含まれることを確認
            assert "quality_management" in all_config or len(all_config) >= 0


class TestMorphologicalAnalyzer:
    """形態素解析器テストの統合"""

    @pytest.fixture
    def sample_text(self) -> str:
        """テスト用サンプルテキスト"""
        return "太郎は花子に本を渡しました。とても嬉しそうでした。"

    def test_basic_analysis(self, sample_text) -> None:
        """基本的な形態素解析"""
        # MorphologicalAnalyzerのテストをここに統合
        # 実際の実装に応じて調整が必要
        assert sample_text is not None

    def test_pos_distribution(self, sample_text) -> None:
        """品詞分布の分析"""
        # 品詞分布テストの実装
        assert sample_text is not None

    def test_sentence_endings(self, sample_text) -> None:
        """文末表現の抽出"""
        # 文末表現テストの実装
        assert sample_text is not None

    def test_readability_metrics(self, sample_text) -> None:
        """読みやすさ指標の計算"""
        # 読みやすさ指標テストの実装
        assert sample_text is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
