"""
测试 web_tools.py WebTools.__init__ — 配置解析优先级

验证:
1. settings.tools.search_api_url (YAML + env merge) 优先
2. os.getenv("SEARCHXNG_URL") 兜底
3. enable_web_search=False 使 enabled=False
4. 无任何配置时 enabled=False + url=localhost:8080
5. rstrip("/search") 行为
6. settings 导入失败的兜底
"""
import os
import sys
from pathlib import Path
from unittest.mock import patch

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

from aacode.tools.web_tools import WebTools
from aacode.config import settings


class MockSafetyGuard:
    def check(self, *args, **kwargs):
        return True


_PROJECT_PATH = Path(".")


def _make_web_tools():
    return WebTools(_PROJECT_PATH, MockSafetyGuard())


class TestWebToolsConfig:
    """WebTools.__init__ 配置解析测试"""

    def test_settings_url_priority(self):
        """settings 有 URL 且 enable_web_search=True → enabled=True，URL 来自 settings"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = "http://192.168.0.106:8081/search"
            settings.tools.enable_web_search = True

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is True, f"expected enabled=True, got {eng['enabled']}"
            assert eng["url"] == "http://192.168.0.106:8081", f"expected rstrip /search, got {eng['url']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_settings_url_no_trailing_search(self):
        """settings URL 不带 /search 后缀"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = "http://192.168.0.106:8081"
            settings.tools.enable_web_search = True

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is True
            assert eng["url"] == "http://192.168.0.106:8081"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_enable_web_search_false_disables(self):
        """enable_web_search=False → enabled=False 即使有 URL"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = "http://192.168.0.106:8081"
            settings.tools.enable_web_search = False

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is False, f"expected enabled=False when enable_web_search=False, got {eng['enabled']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_no_config_defaults(self):
        """无 settings 且无 env → enabled=False + url=localhost:8080"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = None
            settings.tools.enable_web_search = True

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is False, f"expected enabled=False, got {eng['enabled']}"
            assert eng["url"] == "http://localhost:8080", f"expected default url, got {eng['url']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_env_var_fallback_when_settings_none(self):
        """settings URL 为 None，env var 有值 → env var 兜底"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search

        try:
            settings.tools.search_api_url = None
            settings.tools.enable_web_search = True
            os.environ["SEARCHXNG_URL"] = "http://10.0.0.1:9090"

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is True, f"expected enabled=True from env var, got {eng['enabled']}"
            assert eng["url"] == "http://10.0.0.1:9090", f"expected env url, got {eng['url']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            del os.environ["SEARCHXNG_URL"]

    def test_env_var_fallback_rstrip_search(self):
        """env var URL 带 /search 后缀 → rstrip 生效"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search

        try:
            settings.tools.search_api_url = None
            settings.tools.enable_web_search = True
            os.environ["SEARCHXNG_URL"] = "http://10.0.0.1:9090/search"

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is True
            assert eng["url"] == "http://10.0.0.1:9090", f"expected rstrip /search, got {eng['url']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            del os.environ["SEARCHXNG_URL"]

    def test_settings_takes_priority_over_env(self):
        """settings 和 env 都有值时，settings 优先（settings 内部已 merge env 覆盖）"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search

        try:
            # settings 的值是 Settings.__init__ 中 _load_from_env 已做过 env 覆盖的结果
            # 这里模拟正常流程：settings 已有 env 合并后的值
            settings.tools.search_api_url = "http://from-config:8081"
            settings.tools.enable_web_search = True
            os.environ["SEARCHXNG_URL"] = "http://from-env:8081"

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is True
            # settings 优先
            assert eng["url"] == "http://from-config:8081", f"settings should take priority, got {eng['url']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            del os.environ["SEARCHXNG_URL"]

    def test_settings_import_failure_falls_back_to_env(self):
        """模拟 settings 导入失败 → env var 兜底"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search

        try:
            settings.tools.search_api_url = "http://from-config:8081"
            settings.tools.enable_web_search = True
            os.environ["SEARCHXNG_URL"] = "http://from-env:9090"

            with patch(
                "aacode.tools.web_tools.WebTools.__init__",
                autospec=True,
            ) as mock_init:

                def side_effect(self, project_path, safety_guard):
                    self.project_path = project_path
                    self.safety_guard = safety_guard
                    self.session = None
                    env_url = os.getenv("SEARCHXNG_URL")
                    config_url = None
                    enable_web_search = True
                    # 模拟 settings 导入失败
                    # config_url and enable_web_search stay default
                    if config_url:
                        base_url = config_url.rstrip("/search")
                    elif env_url:
                        base_url = env_url.rstrip("/search")
                    else:
                        base_url = "http://localhost:8080"
                    enabled = bool(config_url or env_url) and enable_web_search
                    self.search_engines = {
                        "searxng": {
                            "url": base_url,
                            "enabled": enabled,
                            "description": "",
                        }
                    }
                    self.last_search_time = {}

                mock_init.side_effect = side_effect

                # 直接用构造逻辑等价验证
                env_url = os.getenv("SEARCHXNG_URL")
                config_url = None  # 模拟 settings 不可用
                enable_web_search = True  # 默认值

                if config_url:
                    base_url = config_url.rstrip("/search")
                elif env_url:
                    base_url = env_url.rstrip("/search")
                else:
                    base_url = "http://localhost:8080"

                enabled = bool(config_url or env_url) and enable_web_search
                assert enabled is True, f"env var should enable when settings fails, got {enabled}"
                assert base_url == "http://from-env:9090", f"should use env url, got {base_url}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            del os.environ["SEARCHXNG_URL"]

    def test_both_none_but_enable_web_search_true(self):
        """无 URL 配置但 enable_web_search=True → enabled=False（无 URL 不可用）"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = None
            settings.tools.enable_web_search = True

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is False, f"no url should mean disabled, got {eng['enabled']}"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_empty_string_url_treated_as_none(self):
        """空字符串 URL → 和 None 一样"""
        saved_url = settings.tools.search_api_url
        saved_enable = settings.tools.enable_web_search
        saved_env = os.environ.pop("SEARCHXNG_URL", None)

        try:
            settings.tools.search_api_url = ""
            settings.tools.enable_web_search = True

            wt = _make_web_tools()
            eng = wt.search_engines["searxng"]
            assert eng["enabled"] is False, f"empty url should mean disabled, got {eng['enabled']}"
            assert eng["url"] == "http://localhost:8080"
        finally:
            settings.tools.search_api_url = saved_url
            settings.tools.enable_web_search = saved_enable
            if saved_env is not None:
                os.environ["SEARCHXNG_URL"] = saved_env

    def test_search_engine_description_present(self):
        """description 字段存在且为非空"""
        wt = _make_web_tools()
        eng = wt.search_engines["searxng"]
        assert "description" in eng
        assert isinstance(eng["description"], str)
        assert len(eng["description"]) > 0

    def test_last_search_time_initialized(self):
        """last_search_time 字典正确初始化"""
        wt = _make_web_tools()
        assert isinstance(wt.last_search_time, dict)
        assert len(wt.last_search_time) == 0


if __name__ == "__main__":
    print("=== TestWebToolsConfig ===")
    test = TestWebToolsConfig()
    results = []
    for name in sorted(dir(test)):
        if name.startswith("test_"):
            try:
                getattr(test, name)()
                print(f"  {name}: PASS")
                results.append(("PASS", name))
            except AssertionError as e:
                print(f"  {name}: FAIL - {e}")
                results.append(("FAIL", name))
            except Exception as e:
                print(f"  {name}: ERROR - {e}")
                results.append(("ERROR", name))

    passed = sum(1 for r, _ in results if r == "PASS")
    failed = sum(1 for r, _ in results if r != "PASS")
    print(f"\nTotal: {len(results)}  Passed: {passed}  Failed: {failed}")
