"""
测试配置持久化 —— com.aacode 新路径、自动重建、搜索优先级、环境变量优先
"""
import os
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

import pytest
import yaml
import platformdirs as pd


class TestConfigPersistence:
    """配置持久化路径与自动重建"""

    def test_platformdirs_path_format(self):
        """验证 platformdirs 路径包含 com.aacode"""
        config_dir = pd.user_config_dir("com.aacode", roaming=True)
        assert "com.aacode" in str(config_dir)

    def test_auto_recreate_when_no_config_found(self, monkeypatch):
        """无任何配置时自动从包内默认配置复制到 platformdirs 路径"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            from aacode.config import Settings
            monkeypatch.setattr(Settings, "_find_config_file", staticmethod(lambda *a, **kw: (None, "none")))
            s = Settings()
            assert s.config_path.exists(), "配置文件应被自动创建"
            assert str(td) in str(s.config_path), "配置文件应在 platformdirs 路径下"
            assert s.config_path.name == "aacode_config.yaml"
            assert s.model.max_tokens == 96000

    def test_recreate_does_not_overwrite_existing(self, monkeypatch):
        """已有配置文件时不会覆盖"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            cfg_path = Path(td) / "aacode_config.yaml"
            cfg_path.parent.mkdir(parents=True, exist_ok=True)
            cfg_path.write_text(yaml.dump({"model": {"name": "existing", "max_tokens": 999}}), encoding="utf-8")
            from aacode.config import Settings
            s = Settings()
            assert s.model.name == "existing"
            assert s.model.max_tokens == 999

    def test_load_platformdirs_config_when_present(self, monkeypatch):
        """platformdirs 路径有自定义配置时加载（优先于 package 默认）"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            custom = {"model": {"name": "custom-model", "max_tokens": 12345}}
            cfg_path = Path(td) / "aacode_config.yaml"
            cfg_path.parent.mkdir(parents=True, exist_ok=True)
            cfg_path.write_text(yaml.dump(custom), encoding="utf-8")
            from aacode.config import Settings
            s = Settings()
            assert s.model.name == "custom-model"
            assert s.model.max_tokens == 12345

    def test_search_order_cwd_over_platformdirs(self, monkeypatch):
        """cwd 配置优先于 platformdirs"""
        with tempfile.TemporaryDirectory() as cwd_td:
            with tempfile.TemporaryDirectory() as pd_td:
                monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(pd_td))
                pd_cfg = Path(pd_td) / "aacode_config.yaml"
                pd_cfg.parent.mkdir(parents=True, exist_ok=True)
                pd_cfg.write_text(yaml.dump({"model": {"name": "from-pd"}}), encoding="utf-8")
                cwd_cfg = Path(cwd_td) / "aacode_config.yaml"
                cwd_cfg.write_text(yaml.dump({"model": {"name": "from-cwd"}}), encoding="utf-8")
                from aacode.config import Settings
                orig_cwd = os.getcwd()
                try:
                    os.chdir(cwd_td)
                    s = Settings()
                    assert s.model.name == "from-cwd"
                finally:
                    os.chdir(orig_cwd)

    def test_search_order_platformdirs_over_package(self, monkeypatch):
        """platformdirs 配置优先于 package 默认配置"""
        with tempfile.TemporaryDirectory() as pd_td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(pd_td))
            pd_cfg = Path(pd_td) / "aacode_config.yaml"
            pd_cfg.parent.mkdir(parents=True, exist_ok=True)
            pd_cfg.write_text(yaml.dump({"model": {"name": "from-pd-override"}}), encoding="utf-8")
            from aacode.config import Settings
            s = Settings()
            assert s.model.name == "from-pd-override"

    def test_search_order_package_over_home(self, monkeypatch):
        """package 默认配置优先于 ~/.aacode/（开发环境下 package 存在）"""
        with tempfile.TemporaryDirectory() as pd_td:
            with tempfile.TemporaryDirectory() as home_td:
                monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(pd_td))
                monkeypatch.setattr(Path, "home", lambda: Path(home_td))
                home_cfg = Path(home_td) / ".aacode" / "aacode_config.yaml"
                home_cfg.parent.mkdir(parents=True, exist_ok=True)
                home_cfg.write_text(yaml.dump({"model": {"name": "from-home"}}), encoding="utf-8")
                from aacode.config import Settings
                s = Settings()
                assert s.model.name != "from-home"
                assert s.config_source == "package"

    def test_env_var_overrides_yaml(self, monkeypatch):
        """环境变量 LLM_MODEL_NAME 覆盖配置文件中的值"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            cfg_path = Path(td) / "aacode_config.yaml"
            cfg_path.parent.mkdir(parents=True, exist_ok=True)
            cfg_path.write_text(yaml.dump({"model": {"name": "from-yaml"}}), encoding="utf-8")
            from aacode.config import Settings
            os.environ["LLM_MODEL_NAME"] = "from-env"
            try:
                s = Settings()
                assert s.model.name == "from-env"
            finally:
                del os.environ["LLM_MODEL_NAME"]

    def test_config_source_auto_recreate(self, monkeypatch):
        """自动重建后 config_source 标记为 platformdirs"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            from aacode.config import Settings
            monkeypatch.setattr(Settings, "_find_config_file", staticmethod(lambda *a, **kw: (None, "none")))
            s = Settings()
            assert s.config_source == "platformdirs"

    def test_config_source_explicit_exists(self, monkeypatch):
        """显式指定存在的配置文件时 source 为 explicit"""
        with tempfile.TemporaryDirectory() as td:
            cfg_path = Path(td) / "explicit_config.yaml"
            cfg_path.write_text(yaml.dump({"model": {"name": "explicit-test"}}), encoding="utf-8")
            from aacode.config import Settings
            s = Settings(config_file=str(cfg_path))
            assert s.config_source == "explicit"
            assert s.model.name == "explicit-test"

    def test_config_source_package_default(self):
        """无自定义配置时 source 为 package（使用包内默认）"""
        from aacode.config import Settings
        s = Settings()
        assert s.config_source in ("package", "platformdirs")

    def test_config_api_uses_platformdirs(self, monkeypatch):
        """ConfigAPI 使用新的 platformdirs 路径"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            from aacode.server.api import ConfigAPI
            api = ConfigAPI()
            assert str(td) in str(api.config_path)
            assert api.config_path.name == "aacode_config.yaml"

    def test_config_api_save_and_reload(self, monkeypatch):
        """ConfigAPI 写入新路径后能重新读取"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            from aacode.server.api import ConfigAPI
            api = ConfigAPI()
            api.save_config({"model": {"name": "saved-model", "api_key": "sk-test"}})
            saved = Path(td) / "aacode_config.yaml"
            assert saved.exists()
            loaded = api.get_config()
            assert loaded["model"]["name"] == "saved-model"
            assert loaded["model"]["api_key"] == "sk-test"

    def test_config_api_recent_projects_uses_platformdirs(self, monkeypatch):
        """ConfigAPI 的 recent_projects 也在新路径下"""
        with tempfile.TemporaryDirectory() as td:
            monkeypatch.setattr(pd, "user_config_dir", lambda *a, **kw: str(td))
            from aacode.server.api import ConfigAPI
            api = ConfigAPI()
            assert str(td) in str(api.recent_projects_path)
            assert api.recent_projects_path.name == "recent_projects.json"
