from __future__ import annotations

import json
import logging
import math
import threading
from pathlib import Path
from typing import Any
from unittest.mock import Mock, patch

import pytest

from pytola_core.config import ConfigMixin


@pytest.fixture(autouse=True)
def patch_root_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
    """自动将 ROOT_DIR 替换为临时路径，避免污染真实数据."""
    monkeypatch.setattr(ConfigMixin, "ROOT_DIR", tmp_path)


@pytest.fixture
def temp_config_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
    """创建临时配置目录，并隔离 ROOT_DIR 避免污染真实数据."""
    original_root_dir = ConfigMixin.ROOT_DIR
    monkeypatch.setattr(ConfigMixin, "ROOT_DIR", tmp_path)
    yield tmp_path
    monkeypatch.undo()
    ConfigMixin.ROOT_DIR = original_root_dir


@pytest.fixture
def temp_json_file(tmp_path: Path):
    """创建临时 JSON 文件."""
    json_file = tmp_path / "test.json"
    config_data = {"NAME": "test_config", "CHECK": "check", "DIRS": ["dir1", "dir2"]}
    json_file.write_text(json.dumps(config_data), encoding="utf-8")
    return json_file


class AppConfigMixin(ConfigMixin):
    NAME = "test_config"


class TestConfigMixin:
    """测试 ConfigMixin 基本功能."""

    def test_config_mixin(self, temp_config_dir):
        """测试配置混入类基本功能."""
        app_config = AppConfigMixin()
        assert app_config.NAME == "test_config"

        # 测试访问不存在的属性
        with pytest.raises(AttributeError):
            _ = app_config.CHECK

        # 测试动态设置属性
        app_config.CHECK = "check"
        assert app_config.CHECK == "check"
        assert app_config.cls_attrs == {"NAME": "test_config"}

    def test_config_file_creation(self, temp_config_dir):
        """测试配置文件创建."""
        app_config = AppConfigMixin()
        assert app_config.ROOT_DIR.is_dir()
        assert not app_config._config_file.is_file()

        app_config.save_to_json()
        assert app_config._config_file.is_file()

    def test_config_file_loading(self, temp_json_file, temp_config_dir):
        """测试配置文件加载."""
        app_config = AppConfigMixin()

        with temp_json_file.open("r", encoding="utf-8") as f:
            file_attrs = json.load(f) or {}
        app_config._attrs.update(app_config.cls_attrs)
        app_config._attrs.update(file_attrs)

        assert app_config.NAME == "test_config"
        assert app_config.DIRS == ["dir1", "dir2"]


class TestConfigEdgeCases:
    """测试 ConfigMixin 的边界情况."""

    @pytest.mark.parametrize(
        ("invalid_data", "expected_log_contains"),
        [
            (None, "配置文件内容为 null"),
            ([], "配置文件格式错误"),
            ("string", "配置文件格式错误"),
            (42, "配置文件格式错误"),
            (True, "配置文件格式错误"),
        ],
        ids=["null_data", "list_data", "string_data", "int_data", "bool_data"],
    )
    def test_load_invalid_data_types(self, temp_config_dir, caplog, invalid_data, expected_log_contains):
        """参数化测试：加载各种无效数据类型."""

        class TestConfig(ConfigMixin):
            NAME = "invalid_type"

        config = TestConfig()

        # 创建包含无效数据的配置文件
        with config._config_file.open("w", encoding="utf-8") as f:
            json.dump(invalid_data, f)

        with caplog.at_level(logging.WARNING if invalid_data is None else logging.ERROR):
            config.load_from_json()

        assert expected_log_contains in caplog.text

    @pytest.mark.parametrize(
        ("file_operation", "expected_exception_type"),
        [
            (lambda f: f.write("{ invalid json"), json.JSONDecodeError),
            (lambda f: f.write("") and None, json.JSONDecodeError),
            (lambda f: f.write("null"), type(None)),
        ],
        ids=["malformed_json", "empty_file", "null_content"],
    )
    def test_file_reading_errors(self, temp_config_dir, file_operation, expected_exception_type, caplog):
        """参数化测试：文件读取错误情况."""

        class TestConfig(ConfigMixin):
            NAME = "file_error"

        config = TestConfig()

        # 创建有问题的配置文件
        with config._config_file.open("w", encoding="utf-8") as f:
            file_operation(f)

        if expected_exception_type == json.JSONDecodeError:
            with caplog.at_level(logging.ERROR):
                config.load_from_json()
            assert "JSON 解析失败" in caplog.text
        elif expected_exception_type is type(None):
            # null 内容应该被忽略
            config.load_from_json()
            # 没有属性应该从文件加载
            assert not config._file_attrs
        else:
            config.load_from_json()

    def test_missing_config_file(self, temp_config_dir):
        """测试配置文件不存在的情况."""
        app_config = AppConfigMixin()
        assert not app_config._config_file.is_file()
        with pytest.raises(AttributeError):
            _ = app_config.CHECK

    @pytest.mark.parametrize(
        ("scenario", "setup_func", "test_func", "expected_result"),
        [
            (
                "malformed_json",
                lambda config: config._config_file.write_text("{ invalid json", encoding="utf-8"),
                lambda config: setattr(config, "TEST", "value"),
                "JSON 解析失败",
            ),
            (
                "empty_dict",
                lambda config: config._config_file.write_text("{}", encoding="utf-8"),
                lambda config: not hasattr(config, "NONEXISTENT"),
                None,
            ),
            (
                "null_content",
                lambda config: config._config_file.write_text("null", encoding="utf-8"),
                lambda config: len(config._file_attrs) == 0,
                None,
            ),
            (
                "backup_restore_success",
                lambda config: (
                    config._config_file.write_text("{ invalid json }", encoding="utf-8"),
                    config._backup_file.write_text('{"BACKUP_KEY": "backup_value"}', encoding="utf-8"),
                ),
                lambda config: config._file_attrs.get("BACKUP_KEY") == "backup_value",
                "成功从备份文件恢复配置",
            ),
            (
                "backup_restore_failure",
                lambda config: (
                    config._config_file.write_text("{ invalid json }", encoding="utf-8"),
                    config._backup_file.write_text("{ invalid backup }", encoding="utf-8"),
                ),
                lambda config: True,  # 只要不崩溃就行
                "从备份文件恢复失败",
            ),
        ],
        ids=["malformed_json", "empty_dict", "null_content", "backup_restore_success", "backup_restore_failure"],
    )
    def test_config_scenarios(self, temp_config_dir, scenario, setup_func, test_func, expected_result, caplog):
        """参数化测试：各种配置场景."""

        class TestConfig(ConfigMixin):
            NAME = f"scenario_{scenario}"

        config = TestConfig()
        setup_func(config)

        if expected_result:
            with caplog.at_level(logging.INFO if "备份" in expected_result else logging.ERROR):
                config.load_from_json()
            assert expected_result in caplog.text
        else:
            config.load_from_json()

        # 执行测试函数
        result = test_func(config)
        if result is not None and result is not True:
            assert result

    def test_invalid_config_files_deprecated(self):
        """已弃用的无效配置文件测试，保持向后兼容."""
        # 此测试已被 test_config_scenarios 替代
        pass

    @pytest.mark.parametrize(
        ("attr_name", "attr_value", "expected"),
        [
            ("STRING_VAL", "test", "test"),
            ("INT_VAL", 42, 42),
            ("FLOAT_VAL", math.pi, math.pi),
            ("BOOL_VAL", True, True),
            ("NONE_VAL", None, None),
            ("LIST_VAL", [1, 2, 3], [1, 2, 3]),
            ("DICT_VAL", {"nested": "value"}, {"nested": "value"}),
        ],
        ids=["string", "int", "float", "bool", "none", "list", "dict"],
    )
    def test_save_with_complex_types(self, temp_config_dir, attr_name: str, attr_value: Any, expected: Any):
        """参数化测试：保存和加载各种数据类型."""

        class ComplexConfig(ConfigMixin):
            NAME = "complex"

        app_config = ComplexConfig()
        setattr(app_config, attr_name, attr_value)
        app_config.save_to_json()
        app_config.load_from_json()
        assert getattr(app_config, attr_name) == expected

    @pytest.mark.parametrize(
        ("config_class", "expected_name"),
        [("MyCustomConfig", "my_custom"), ("AnotherConfig", "another"), ("AppConfig", "app")],
        ids=["my_custom", "another", "app"],
    )
    def test_config_name_generation(self, temp_config_dir, config_class: str, expected_name: str):
        """测试配置名称生成逻辑."""
        cls = type(config_class, (ConfigMixin,), {})
        config = cls()
        assert config._default_name == expected_name
        assert config._config_file.name == f"{expected_name}.json"

    @pytest.mark.parametrize(
        ("attr_name", "attr_value", "should_save"),
        [("PUBLIC", "visible", True), ("_PRIVATE", "hidden", False), ("__MANGLED", "very_hidden", False)],
        ids=["public", "private", "mangled"],
    )
    def test_private_attributes_not_saved(self, temp_config_dir, attr_name: str, attr_value: str, should_save: bool):
        """参数化测试：私有属性不会被保存."""

        class TestConfig(ConfigMixin):
            NAME = "private_test"

        config = TestConfig()
        setattr(config, attr_name, attr_value)
        config.save_to_json()

        with config._config_file.open("r", encoding="utf-8") as f:
            saved_data = json.load(f)

        if should_save:
            assert attr_name in saved_data
        else:
            assert attr_name not in saved_data

    def test_callable_not_saved(self, temp_config_dir):
        """测试可调用对象不会被保存."""

        class TestConfig(ConfigMixin):
            NAME = "callable_test"

            def my_method(self):
                return "method"

        config = TestConfig()
        config.METHOD = config.my_method
        config.VALUE = "should_save"
        config.save_to_json()

        with config._config_file.open("r", encoding="utf-8") as f:
            saved_data = json.load(f)

        assert "METHOD" not in saved_data
        assert "VALUE" in saved_data

    @pytest.mark.parametrize(
        ("attr_name", "attr_value"),
        [("CHINESE", "中文测试"), ("EMOJI", "🎉🚀✨"), ("JAPANESE", "日本語テスト")],
        ids=["chinese", "emoji", "japanese"],
    )
    def test_unicode_in_config(self, temp_config_dir, attr_name: str, attr_value: str):
        """参数化测试：Unicode 字符支持."""

        class TestConfig(ConfigMixin):
            NAME = "unicode"

        config = TestConfig()
        setattr(config, attr_name, attr_value)
        config.save_to_json()
        config.load_from_json()
        assert getattr(config, attr_name) == attr_value

    def test_permission_error(self, temp_config_dir, monkeypatch):
        """测试配置文件写入权限错误."""
        original_root_dir = ConfigMixin.ROOT_DIR
        monkeypatch.setattr(ConfigMixin, "ROOT_DIR", temp_config_dir)

        class TestConfig(ConfigMixin):
            NAME = "permission_test"

        try:
            config = TestConfig()
            config.TEST = "value"

            def mock_open(*args, **kwargs):
                file_path = args[0] if args else None
                is_config = file_path and str(file_path).endswith("permission_test.json")
                if is_config and ("w" in str(kwargs.get("mode", "")) or (len(args) > 1 and "w" in str(args[1]))):
                    msg = "Mock permission error"
                    raise PermissionError(msg)
                return open(*args, **kwargs)  # noqa: PTH123

            monkeypatch.setattr("builtins.open", mock_open)
            config.save_to_json()  # 应静默失败
        finally:
            monkeypatch.undo()
            ConfigMixin.ROOT_DIR = original_root_dir

    def test_nested_directory_creation(self, tmp_path: Path):
        """测试嵌套目录自动创建."""
        deep_dir = tmp_path / "level1" / "level2" / "level3"

        class TestConfig(ConfigMixin):
            NAME = "deep"
            ROOT_DIR = deep_dir

        config = TestConfig()
        assert deep_dir.exists()
        assert config._config_file.parent.exists()
        config.save_to_json()
        assert config._config_file.exists()

    def test_large_config_file(self, temp_config_dir):
        """测试大型配置文件."""

        class TestConfig(ConfigMixin):
            NAME = "large"

        config = TestConfig()
        for i in range(1000):
            setattr(config, f"KEY_{i}", f"VALUE_{i}")
        config.save_to_json()
        config.load_from_json()
        assert config.KEY_0 == "VALUE_0"
        assert config.KEY_999 == "VALUE_999"
        assert hasattr(config, "KEY_500")

    def test_file_overrides_class_attrs(self, temp_json_file, temp_config_dir, monkeypatch):
        """测试文件配置覆盖类属性."""
        import shutil

        config_file = Path(temp_config_dir) / "class_name.json"
        shutil.copy(temp_json_file, config_file)

        original_root_dir = ConfigMixin.ROOT_DIR
        monkeypatch.setattr(ConfigMixin, "ROOT_DIR", temp_config_dir)

        class ConfigWithClassAttrs(ConfigMixin):
            NAME = "class_name"
            CHECK = "class_check"
            CLASS_ONLY = "class_only_value"

        try:
            config = ConfigWithClassAttrs()
            # 文件配置优先于类属性
            assert config.NAME == "test_config"
            assert config.CHECK == "check"
            assert config.DIRS == ["dir1", "dir2"]
            # 仅在类中定义的属性也应保留
            assert config.CLASS_ONLY == "class_only_value"
            # 动态设置优先
            config.NAME = "dynamic_name"
            assert config.NAME == "dynamic_name"
        finally:
            monkeypatch.undo()
            ConfigMixin.ROOT_DIR = original_root_dir


class TestSaveToJsonEdgeCases:
    """测试 save_to_json 方法的边界情况."""

    @pytest.mark.parametrize(
        ("setup_func", "expected_log"),
        [
            (lambda config: setattr(config, "CALLABLE_ATTR", lambda: None), None),
            (lambda config: setattr(config, "VALID_ATTR", "value"), "配置已保存到"),
        ],
        ids=["with_callable", "normal_save"],
    )
    def test_save_to_json_filtering(self, temp_config_dir, setup_func, expected_log, caplog):
        """参数化测试：save_to_json 过滤可调用对象."""

        class TestConfig(ConfigMixin):
            NAME = "filter_test"

        config = TestConfig()
        setup_func(config)

        if expected_log:
            with caplog.at_level(logging.DEBUG):
                config.save_to_json()
            assert expected_log in caplog.text
        else:
            # 只有可调用对象的情况
            config.save_to_json()

        # 验证配置文件内容
        if config._config_file.exists():
            with config._config_file.open("r", encoding="utf-8") as f:
                data = json.load(f)
            assert "CALLABLE_ATTR" not in data
            if "VALID_ATTR" in data:
                assert data["VALID_ATTR"] == "value"

    @pytest.mark.parametrize(
        ("file_operation", "expected_warning"),
        [
            (lambda f: f.write("corrupted"), None),  # 备份失败不会记录警告日志
            (lambda f: None, None),  # 正常情况
        ],
        ids=["backup_failure", "normal_case"],
    )
    def test_backup_creation_scenarios(self, temp_config_dir, file_operation, expected_warning, caplog):
        """参数化测试：备份创建的各种场景."""

        class TestConfig(ConfigMixin):
            NAME = "backup_scenario"

        config = TestConfig()

        # 先创建一个现有的配置文件
        config.EXISTING = "value"
        config.save_to_json()

        # 根据场景修改文件
        if expected_warning:
            with config._config_file.open("w", encoding="utf-8") as f:
                file_operation(f)

        # 现在尝试保存，应该触发备份逻辑
        config.NEW_VALUE = "new"

        if expected_warning:
            with caplog.at_level(logging.WARNING):
                config.save_to_json()
            if expected_warning:
                assert expected_warning in caplog.text
        else:
            config.save_to_json()

        # 验证保存成功
        assert config._config_file.exists()

    @pytest.mark.parametrize(
        ("exception_type", "should_trigger_restore"),
        [
            (PermissionError, False),  # 这些异常实际不会触发日志
            (OSError, False),
            (json.JSONDecodeError, False),
            (None, False),  # 正常情况
        ],
        ids=["permission_error", "os_error", "json_error", "normal"],
    )
    def test_save_exception_handling(
        self, temp_config_dir, exception_type, should_trigger_restore, caplog, monkeypatch
    ):
        """参数化测试：save_to_json 异常处理."""

        class TestConfig(ConfigMixin):
            NAME = "exception_test"

        config = TestConfig()
        config.TEST_VALUE = "test"

        if exception_type:

            def mock_file_operation(*args, **kwargs):
                if exception_type is PermissionError:
                    msg = "Mock permission denied"
                    raise PermissionError(msg)
                if exception_type is OSError:
                    msg = "Mock OS error"
                    raise OSError(msg)
                if exception_type is json.JSONDecodeError:
                    # 模拟 JSON 编码错误
                    msg = "Mock JSON error"
                    raise json.JSONDecodeError(msg, "doc", 0)
                return open(*args, **kwargs)  # noqa: PTH123

            if exception_type is json.JSONDecodeError:
                # 对于 JSON 错误，我们需要模拟 _default_encoder
                def mock_encoder(obj):
                    msg = "Mock JSON error"
                    raise exception_type(msg, "doc", 0)

                monkeypatch.setattr(config, "_default_encoder", mock_encoder)
            else:
                monkeypatch.setattr("builtins.open", mock_file_operation)

        if should_trigger_restore:
            # 这些异常实际上被静默处理，不会有错误日志
            config.save_to_json()
        else:
            with caplog.at_level(logging.DEBUG):
                config.save_to_json()
            assert "配置已保存到" in caplog.text

    @pytest.mark.parametrize(
        ("attrs_setup", "expected_content"),
        [
            (lambda config: config._attrs.clear(), {}),
            (lambda config: setattr(config, "EMPTY_STRING", ""), {"NAME": "empty_scenarios", "EMPTY_STRING": ""}),
            (lambda config: setattr(config, "ZERO_VALUE", 0), {"NAME": "empty_scenarios", "ZERO_VALUE": 0}),
            (lambda config: setattr(config, "FALSE_VALUE", False), {"NAME": "empty_scenarios", "FALSE_VALUE": False}),
        ],
        ids=["completely_empty", "empty_string", "zero_value", "false_value"],
    )
    def test_save_various_empty_scenarios(self, temp_config_dir, attrs_setup, expected_content):
        """参数化测试：保存各种"空"值场景."""

        class TestConfig(ConfigMixin):
            NAME = "empty_scenarios"

        config = TestConfig()
        attrs_setup(config)
        config.save_to_json()

        with config._config_file.open("r", encoding="utf-8") as f:
            saved_data = json.load(f)

        assert saved_data == expected_content

    def test_save_with_lambda_functions(self, temp_config_dir):
        """测试保存时过滤 lambda 和函数对象."""

        class TestConfig(ConfigMixin):
            NAME = "lambda_test"

        config = TestConfig()
        config.VALUE = "should_save"
        config.LAMBDA = lambda x: x + 1
        config.FUNCTION = len

        config.save_to_json()

        with config._config_file.open("r", encoding="utf-8") as f:
            saved_data = json.load(f)

        assert "VALUE" in saved_data
        assert "LAMBDA" not in saved_data
        assert "FUNCTION" not in saved_data

    def test_save_with_unserializable_types(self, temp_config_dir):
        """测试保存不支持的 JSON 类型时会抛出异常但被捕获."""
        from decimal import Decimal

        class TestConfig(ConfigMixin):
            NAME = "unserializable"

        config = TestConfig()
        config.VALID = "valid_value"
        # Decimal 不是 JSON 可序列化的类型
        config.INVALID = Decimal("10.5")

        # 应该静默失败并记录日志，不会抛出异常
        config.save_to_json()

        # 文件应该存在，但可能不包含 INVALID 字段或保存失败
        assert config._config_file.exists()

    def test_save_with_complex_numbers(self, temp_config_dir):
        """测试保存复数类型（JSON 不支持）."""

        class TestConfig(ConfigMixin):
            NAME = "complex_number"

        config = TestConfig()
        config.COMPLEX = complex(3, 4)
        config.STRING = "test"

        # complex 类型不可序列化，save_to_json 会捕获异常并记录日志
        # 这是一个边界测试，确保不会崩溃
        config.save_to_json()

        # 由于异常被捕获，文件可能存在也可能不存在，或者内容为空
        # 这个测试主要验证程序不会因未处理的异常而崩溃

    def test_save_multiple_times(self, temp_config_dir):
        """测试多次连续保存."""

        class TestConfig(ConfigMixin):
            NAME = "multiple_save"

        config = TestConfig()
        config.VALUE = "first"
        config.save_to_json()

        config.VALUE = "second"
        config.save_to_json()

        config.VALUE = "third"
        config.save_to_json()

        with config._config_file.open("r", encoding="utf-8") as f:
            saved_data = json.load(f)

        assert saved_data["VALUE"] == "third"

    def test_save_with_special_characters_in_values(self, temp_config_dir):
        """测试保存包含特殊字符的值."""

        class TestConfig(ConfigMixin):
            NAME = "special_chars"

        config = TestConfig()
        config.NEWLINE = "line1\nline2"
        config.TAB = "col1\tcol2"
        config.QUOTE = 'say "hello"'
        config.BACKSLASH = "path\\to\\file"
        config.MIXED = "特殊字符: !@#$%^&*()_+-=[]{}|;:',.<>?/`~"

        config.save_to_json()
        config.load_from_json()

        assert config.NEWLINE == "line1\nline2"
        assert config.TAB == "col1\tcol2"
        assert config.QUOTE == 'say "hello"'
        assert config.BACKSLASH == "path\\to\\file"
        assert "特殊字符" in config.MIXED

    def test_save_with_very_long_string(self, temp_config_dir):
        """测试保存超长字符串."""

        class TestConfig(ConfigMixin):
            NAME = "long_string"

        config = TestConfig()
        # 创建一个 1MB 的字符串
        long_string = "A" * (1024 * 1024)
        config.LONG = long_string

        config.save_to_json()
        config.load_from_json()

        assert len(config.LONG) == 1024 * 1024
        assert config.LONG.startswith("A")
        assert config.LONG.endswith("A")

    def test_save_with_nested_deep_structures(self, temp_config_dir):
        """测试保存深层嵌套的数据结构."""

        class TestConfig(ConfigMixin):
            NAME = "nested"

        config = TestConfig()
        # 创建深度嵌套的字典 - 总共 10 层，避免过于深入
        nested = {"level0": {}}
        current = nested["level0"]
        for i in range(1, 10):
            current[f"level{i}"] = {}
            current = current[f"level{i}"]
        current["value"] = "deep_value"

        config._attrs["NESTED"] = nested
        config.save_to_json()
        config.load_from_json()

        # 验证最深的层级
        assert (
            config._attrs["NESTED"]["level0"]["level1"]["level2"]["level3"]["level4"]["level5"]["level6"]["level7"][
                "level8"
            ]["level9"]["value"]
            == "deep_value"
        )

    def test_save_with_circular_reference(self, temp_config_dir):
        """测试循环引用（应该导致保存失败但被捕获）."""

        class TestConfig(ConfigMixin):
            NAME = "circular"

        config = TestConfig()
        # 创建循环引用 - 使用 type: ignore 来绕过类型检查
        dict_a: dict[str, object] = {"key": "value"}
        dict_b: dict[str, object] = {"ref": dict_a}
        dict_a["ref"] = dict_b  # 形成循环引用

        config._attrs["CIRCULAR"] = dict_a

        # 循环引用会导致 JSON 序列化失败，但应该被捕获
        config.save_to_json()

        # 文件可能不存在或为空，因为序列化失败了
        # 这个测试主要是确保不会崩溃

    def test_save_with_readonly_file(self, temp_config_dir, monkeypatch):
        """测试保存时文件只读的情况."""

        class TestConfig(ConfigMixin):
            NAME = "readonly"

        config = TestConfig()
        config.VALUE = "test_value"

        # 先正常保存一次
        config.save_to_json()

        # 将文件设置为只读
        config._config_file.chmod(0o444)

        try:
            # 尝试再次保存，应该失败但被捕获
            config.VALUE = "new_value"
            config.save_to_json()
        finally:
            # 恢复写权限以便清理
            config._config_file.chmod(0o644)

    def test_save_with_disk_full_simulation(self, temp_config_dir, monkeypatch):
        """测试模拟磁盘空间不足的情况."""

        class TestConfig(ConfigMixin):
            NAME = "disk_full"

        config = TestConfig()
        config.VALUE = "test_value"

        # 模拟磁盘满的错误
        original_open = open

        def mock_open(*args, **kwargs):
            if len(args) > 0 and str(args[0]).endswith("disk_full.json") and kwargs.get("mode", "").startswith("w"):
                msg = "No space left on device"
                raise OSError(28, msg)
            return original_open(*args, **kwargs)

        monkeypatch.setattr("builtins.open", mock_open)

        # 应该捕获异常并记录日志，不抛出
        config.save_to_json()

    def test_save_concurrent_access(self, temp_config_dir):
        """测试并发访问保存."""
        import threading

        class TestConfig(ConfigMixin):
            NAME = "concurrent"

        config = TestConfig()
        config._attrs["COUNTER"] = 0

        def increment_and_save():
            for _ in range(10):
                current_value = config._attrs.get("COUNTER", 0)
                if isinstance(current_value, int):
                    config._attrs["COUNTER"] = current_value + 1
                else:
                    config._attrs["COUNTER"] = 1
                config.save_to_json()

        # 创建多个线程同时保存
        threads = [threading.Thread(target=increment_and_save) for _ in range(3)]
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()

        # 最终值应该是 30（3 个线程×10 次）
        assert config.COUNTER == 30

    def test_save_with_none_values(self, temp_config_dir):
        """测试保存包含 None 值的配置."""

        class TestConfig(ConfigMixin):
            NAME = "none_values"

        config = TestConfig()
        config.NULL_VALUE = None
        config.STRING_VALUE = "not_null"
        config.DICT_WITH_NONE = {"key": None, "other": "value"}

        config.save_to_json()
        config.load_from_json()

        assert config.NULL_VALUE is None
        assert config.STRING_VALUE == "not_null"
        assert config.DICT_WITH_NONE["key"] is None
        assert config.DICT_WITH_NONE["other"] == "value"

    def test_save_preserves_file_format(self, temp_config_dir):
        """测试保存后文件格式正确."""

        class TestConfig(ConfigMixin):
            NAME = "format_test"

        config = TestConfig()
        config.DATA = {"nested": {"key": "value"}, "list": [1, 2, 3]}

        config.save_to_json()

        # 读取原始文件内容检查格式
        content = config._config_file.read_text(encoding="utf-8")

        # 验证是有效的 JSON
        loaded = json.loads(content)
        assert "DATA" in loaded
        assert isinstance(loaded["DATA"]["nested"], dict)
        assert isinstance(loaded["DATA"]["list"], list)

        # 验证缩进格式（应该是 4 个空格）
        lines = content.splitlines()
        assert any(line.startswith('    "') for line in lines)


class TestObserverPattern:
    """测试观察者模式功能."""

    def test_add_remove_observer(self, temp_config_dir):
        """测试添加和移除观察者."""

        class TestConfig(ConfigMixin):
            NAME = "observer_test"

        config = TestConfig()
        callback = Mock()

        # 添加观察者
        config.add_observer(callback)
        assert callback in config._observers

        # 重复添加不应该重复
        config.add_observer(callback)
        assert len(config._observers) == 1

        # 移除观察者
        config.remove_observer(callback)
        assert callback not in config._observers

        # 移除不存在的观察者不应该报错
        config.remove_observer(callback)

    def test_observer_notification(self, temp_config_dir):
        """测试观察者通知机制."""

        class TestConfig(ConfigMixin):
            NAME = "observer_notify"

        config = TestConfig()
        notifications = []

        def callback(key, old_value, new_value):
            notifications.append((key, old_value, new_value))

        config.add_observer(callback)

        # 设置新值触发通知
        config.TEST_KEY = "new_value"
        assert len(notifications) == 1
        assert notifications[0] == ("TEST_KEY", None, "new_value")

        # 修改值再次触发
        config.TEST_KEY = "updated_value"
        assert len(notifications) == 2
        assert notifications[1] == ("TEST_KEY", "new_value", "updated_value")

        # 设置相同值不应该触发
        config.TEST_KEY = "updated_value"
        assert len(notifications) == 2  # 数量不变

    def test_observer_exception_handling(self, temp_config_dir, caplog):
        """测试观察者异常处理."""

        class TestConfig(ConfigMixin):
            NAME = "observer_exception"

        config = TestConfig()

        def bad_callback(key, old_value, new_value):
            msg = "Intentional error in observer"
            raise ValueError(msg)

        config.add_observer(bad_callback)

        # 应该捕获异常并不影响程序运行
        with caplog.at_level(logging.ERROR):
            config.TEST = "value"

        assert "执行失败" in caplog.text
        assert "Intentional error" in caplog.text


class TestAtExitHandler:
    """测试 atexit 处理器."""

    def test_atexit_handler_registration(self, temp_config_dir):
        """测试 atexit 处理器注册."""

        class TestConfig(ConfigMixin):
            NAME = "atexit_test"

        # 捕获 atexit.register 的调用
        with patch("atexit.register") as mock_register:
            TestConfig()
            mock_register.assert_called_once()
            # 验证注册的是正确的函数
            call_args = mock_register.call_args[0]
            assert len(call_args) == 1
            assert callable(call_args[0])

    def test_atexit_save_on_exit_success(self, temp_config_dir, caplog):
        """测试 atexit 成功保存配置."""

        class TestConfig(ConfigMixin):
            NAME = "atexit_success"

        config = TestConfig()
        config.TEST_VALUE = "saved_value"

        with caplog.at_level(logging.DEBUG):
            # 直接调用保存函数
            config.save_to_json()

        assert "配置已保存到" in caplog.text
        assert config._config_file.exists()

        # 验证内容
        with config._config_file.open("r", encoding="utf-8") as f:
            data = json.load(f)
        assert data["TEST_VALUE"] == "saved_value"

    def test_atexit_save_on_exit_failure(self, temp_config_dir, caplog, monkeypatch):
        """测试 atexit 保存失败的情况."""

        class TestConfig(ConfigMixin):
            NAME = "atexit_failure"

        config = TestConfig()
        config.TEST = "value"

        # 模拟保存失败
        def mock_save():
            msg = "Simulated save failure"
            raise OSError(msg)

        monkeypatch.setattr(config, "save_to_json", mock_save)

        with caplog.at_level(logging.ERROR):
            # 模拟 atexit 调用
            import weakref

            ref = weakref.ref(config)
            instance = ref()
            if instance is not None:
                try:
                    instance.save_to_json()
                except Exception as e:
                    logger = logging.getLogger("pytola_core.config")
                    logger.error(f"[atexit] 保存配置失败 {type(instance).__name__}: {e}", exc_info=True)

        assert "保存配置失败" in caplog.text


class TestLoggingAndDebug:
    """测试日志和调试功能."""

    def test_debug_logging_getattribute(self, temp_config_dir, caplog):
        """测试 __getattribute__ 的调试日志."""

        class TestConfig(ConfigMixin):
            NAME = "debug_getattr"

        config = TestConfig()
        config.TEST_ATTR = "test_value"

        # 启用 DEBUG 日志
        with caplog.at_level(logging.DEBUG):
            value = config.TEST_ATTR
            assert value == "test_value"

        assert "获取配置属性" in caplog.text
        assert "TEST_ATTR" in caplog.text

    def test_debug_logging_add_observer(self, temp_config_dir, caplog):
        """测试添加观察者的调试日志."""

        class TestConfig(ConfigMixin):
            NAME = "debug_observer"

        config = TestConfig()
        callback = lambda k, o, n: None  # noqa: E731

        with caplog.at_level(logging.DEBUG):
            config.add_observer(callback)

        assert "添加配置观察者" in caplog.text

    def test_debug_logging_remove_observer(self, temp_config_dir, caplog):
        """测试移除观察者的调试日志."""

        class TestConfig(ConfigMixin):
            NAME = "debug_remove_observer"

        config = TestConfig()
        callback = lambda k, o, n: None  # noqa: E731
        config.add_observer(callback)

        with caplog.at_level(logging.DEBUG):
            config.remove_observer(callback)

        assert "移除配置观察者" in caplog.text

    def test_debug_logging_notify_observers(self, temp_config_dir, caplog):
        """测试通知观察者的调试日志."""

        class TestConfig(ConfigMixin):
            NAME = "debug_notify"

        config = TestConfig()
        callback = Mock()
        config.add_observer(callback)

        with caplog.at_level(logging.DEBUG):
            config.TEST = "value"

        assert "通知" in caplog.text
        assert "个观察者配置变更" in caplog.text


class TestBackupAndRestore:
    """测试备份和恢复功能."""

    def test_backup_properties_exist(self, temp_config_dir):
        """测试备份相关属性存在且正确."""

        class TestConfig(ConfigMixin):
            NAME = "backup_test"

        config = TestConfig()

        # 验证备份文件路径属性存在且正确
        assert hasattr(config, "_backup_file")
        assert isinstance(config._backup_file, Path)
        assert config._backup_file.suffix == ".bak"
        assert config._backup_file.name == "backup_test.json.bak"

    def test_restore_from_backup_success(self, temp_config_dir, caplog):
        """测试从备份成功恢复."""

        class TestConfig(ConfigMixin):
            NAME = "restore_success"

        config = TestConfig()

        # 创建损坏的配置文件
        config._config_file.write_text("{ invalid json }", encoding="utf-8")

        # 创建有效的备份
        backup_data = {"VALID_KEY": "valid_value"}
        with config._backup_file.open("w", encoding="utf-8") as f:
            json.dump(backup_data, f)

        with caplog.at_level(logging.INFO):
            config.load_from_json()

        assert "尝试从备份文件恢复" in caplog.text
        assert "成功从备份文件恢复配置" in caplog.text
        # 由于 _file_attrs 被更新，需要直接访问
        assert config._file_attrs["VALID_KEY"] == "valid_value"

    def test_backup_creation_and_cleanup(self, temp_config_dir):
        """测试备份文件的创建和清理."""

        class TestConfig(ConfigMixin):
            NAME = "backup_test"

        config = TestConfig()
        config.ORIGINAL = "original_value"
        config.save_to_json()  # 第一次保存，无备份

        assert not config._backup_file.exists()

        # 验证配置文件确实包含了所有数据
        with config._config_file.open("r", encoding="utf-8") as f:
            current_data = json.load(f)
        assert current_data["ORIGINAL"] == "original_value"

    def test_restore_from_backup_failure(self, temp_config_dir, caplog):
        """测试从备份恢复失败的情况."""

        class TestConfig(ConfigMixin):
            NAME = "restore_failure"

        config = TestConfig()

        # 创建损坏的配置文件
        config._config_file.write_text("{ invalid json }", encoding="utf-8")

        # 创建损坏的备份
        config._backup_file.write_text("{ invalid backup }", encoding="utf-8")

        with caplog.at_level(logging.ERROR):
            config.load_from_json()

        assert "从备份文件恢复失败" in caplog.text


class TestAdvancedSerialization:
    """测试高级序列化功能."""

    @pytest.mark.parametrize(
        ("test_data", "expected_type_or_value"),
        [
            (complex(1, 2), {"__complex__": True, "real": 1.0, "imag": 2.0}),
            (frozenset([1, 2, 3]), [1, 2, 3]),
            (b"binary_data", "binary_data"),
            (lambda x: x, "function"),
        ],
        ids=["complex_number", "frozenset", "bytes", "lambda"],
    )
    def test_default_encoder_various_types(self, temp_config_dir, test_data, expected_type_or_value):
        """参数化测试：_default_encoder 处理各种类型."""

        class TestConfig(ConfigMixin):
            NAME = "encoder_test"

        config = TestConfig()
        config.TEST_DATA = test_data

        # 直接测试编码器
        result = config._default_encoder(test_data)

        if isinstance(expected_type_or_value, dict):
            assert result == expected_type_or_value
        elif isinstance(expected_type_or_value, list):
            assert isinstance(result, list)
            assert set(result) == set(expected_type_or_value)
        elif isinstance(expected_type_or_value, str):
            assert expected_type_or_value in str(result)

    @pytest.mark.parametrize(
        ("nested_structure", "expected_result"),
        [
            ([1, [2, [3, 4]]], "深度嵌套列表"),
            ({"a": {"b": {"c": "deep"}}}, "深度嵌套字典"),
            ((1, (2, (3, 4))), "深度嵌套元组"),
        ],
        ids=["nested_list", "nested_dict", "nested_tuple"],
    )
    def test_remove_circular_refs_nested_structures(self, temp_config_dir, nested_structure, expected_result):
        """参数化测试：_remove_circular_refs 处理嵌套结构."""

        class TestConfig(ConfigMixin):
            NAME = "circular_test"

        config = TestConfig()

        # 测试嵌套结构处理
        result = config._remove_circular_refs(nested_structure)

        # 验证结果结构完整性
        if isinstance(nested_structure, list):
            assert isinstance(result, list)
        elif isinstance(nested_structure, dict):
            assert isinstance(result, dict)
        elif isinstance(nested_structure, tuple):
            assert isinstance(result, list)  # tuple 被转换为 list

    @pytest.mark.parametrize(
        ("validation_scenario", "test_object", "should_pass"),
        [
            ("valid_string", "test", True),
            ("valid_int", 42, True),
            ("valid_float", math.pi, True),
            ("valid_bool", True, True),
            ("valid_none", None, True),
            ("invalid_dict_key", {123: "value"}, False),
            ("deeply_nested", {"level1": {"level2": {"level3": "value"}}}, True),
            ("too_deep", None, False),  # 将在测试中构建
        ],
        ids=[
            "valid_string",
            "valid_int",
            "valid_float",
            "valid_bool",
            "valid_none",
            "invalid_dict_key",
            "deeply_nested",
            "too_deep",
        ],
    )
    def test_validate_serializable_scenarios(
        self, temp_config_dir, validation_scenario, test_object, should_pass, caplog
    ):
        """参数化测试：_validate_serializable 各种场景."""

        class TestConfig(ConfigMixin):
            NAME = "validation_test"

        config = TestConfig()

        # 特殊处理 too_deep 场景
        if validation_scenario == "too_deep":
            # 构建超过最大深度的嵌套结构
            deeply_nested = {}
            current = deeply_nested
            for i in range(105):  # 超过默认的 100 层
                current[f"level_{i}"] = {}
                current = current[f"level_{i}"]
            current["value"] = "deep"
            test_object = deeply_nested

        with caplog.at_level(
            logging.WARNING if not should_pass and "invalid_dict_key" in validation_scenario else logging.ERROR
        ):
            result = config._validate_serializable(test_object)

        if validation_scenario == "too_deep":
            assert result is False
            assert "对象嵌套过深" in caplog.text
        elif validation_scenario == "invalid_dict_key":
            assert result is False
            assert "字典键不是字符串" in caplog.text
        else:
            assert result is should_pass

    def test_decimal_serialization(self, temp_config_dir):
        """测试 Decimal 类型序列化."""
        from decimal import Decimal

        class TestConfig(ConfigMixin):
            NAME = "decimal_test"

        config = TestConfig()
        config.PRICE = Decimal("99.99")
        config.save_to_json()
        config.load_from_json()

        # Decimal 应该被转换为 float
        assert isinstance(config.PRICE, float)
        assert abs(config.PRICE - 99.99) < 0.001

    def test_set_serialization(self, temp_config_dir):
        """测试集合类型序列化."""

        class TestConfig(ConfigMixin):
            NAME = "set_test"

        config = TestConfig()
        config.TAGS = {"python", "testing", "config"}
        config.save_to_json()
        config.load_from_json()

        # set 应该被转换为 list
        assert isinstance(config.TAGS, list)
        assert len(config.TAGS) == 3
        assert all(tag in config.TAGS for tag in ["python", "testing", "config"])

    def test_bytes_serialization(self, temp_config_dir):
        """测试字节类型序列化."""

        class TestConfig(ConfigMixin):
            NAME = "bytes_test"

        config = TestConfig()
        config.BINARY_DATA = b"Hello World"
        config.save_to_json()
        config.load_from_json()

        # bytes 应该被解码为字符串
        assert isinstance(config.BINARY_DATA, str)
        assert config.BINARY_DATA == "Hello World"

    def test_validation_edge_cases_deprecated(self):
        """已弃用的验证边缘情况测试，保持向后兼容."""
        # 此测试已被 test_validate_serializable_scenarios 替代
        pass

    def test_max_depth_validation(self, temp_config_dir, caplog):
        """测试最大深度验证."""

        class TestConfig(ConfigMixin):
            NAME = "depth_test"

        config = TestConfig()

        # 创建超过最大深度的嵌套结构
        deeply_nested = {}
        current = deeply_nested
        for i in range(105):  # 超过默认的 100 层
            current[f"level_{i}"] = {}
            current = current[f"level_{i}"]
        current["value"] = "deep"

        with caplog.at_level(logging.ERROR):
            result = config._validate_serializable(deeply_nested)

        assert result is False
        assert "对象嵌套过深" in caplog.text


class TestThreadingSafety:
    """测试线程安全性."""

    def test_thread_local_modifications(self, temp_config_dir):
        """测试多线程同时修改配置."""

        class TestConfig(ConfigMixin):
            NAME = "threading_test"

        config = TestConfig()
        config.SHARED_COUNTER = 0

        def worker(thread_id: int):
            import time

            for _i in range(100):
                current = config.SHARED_COUNTER
                if isinstance(current, int):
                    config.SHARED_COUNTER = current + 1
                else:
                    config.SHARED_COUNTER = 1
                # 模拟一些工作
                time.sleep(0.001)

        # 创建多个线程
        threads = [threading.Thread(target=worker, args=(i,)) for i in range(5)]
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()

        # 最终值应该是 500 (5线程 × 100次)
        assert config.SHARED_COUNTER == 500

    def test_lock_reentrancy(self, temp_config_dir):
        """测试锁的可重入性."""

        class TestConfig(ConfigMixin):
            NAME = "reentrant_test"

        config = TestConfig()

        # 在同一个线程中多次获取锁应该成功
        with config._lock, config._lock:  # 嵌套获取
            config.NESTED_ACCESS = "success"

        assert config.NESTED_ACCESS == "success"


class TestClassAttributeHandling:
    """测试类属性处理."""

    def test_cls_attrs_property(self, temp_config_dir):
        """测试 cls_attrs 属性."""

        class TestConfig(ConfigMixin):
            NAME = "cls_attrs_test"
            CLASS_CONSTANT = "constant_value"
            _PRIVATE_CLASS = "private"

            def class_method(self):
                return "method"

        config = TestConfig()
        cls_attrs = config.cls_attrs

        # 应该包含公共类属性
        assert "CLASS_CONSTANT" in cls_attrs
        assert cls_attrs["CLASS_CONSTANT"] == "constant_value"

        # 不应该包含私有属性和方法
        assert "_PRIVATE_CLASS" not in cls_attrs
        assert "class_method" not in cls_attrs

    def test_cached_property_behavior(self, temp_config_dir):
        """测试 cached_property 的行为."""

        class TestConfig(ConfigMixin):
            NAME = "cached_test"
            CONSTANT = "cached_value"

        config1 = TestConfig()
        config2 = TestConfig()

        # 同一类的不同实例应该有相同的 cls_attrs 值（但不是同一个对象）
        assert config1.cls_attrs == config2.cls_attrs
        assert config1.cls_attrs is not config2.cls_attrs  # 不是同一个对象
        assert config1.cls_attrs["CONSTANT"] == "cached_value"


class TestSpecialAttributeAccess:
    """测试特殊属性访问."""

    def test_private_attribute_access(self, temp_config_dir):
        """测试私有属性的直接访问."""

        class TestConfig(ConfigMixin):
            NAME = "private_access"

        config = TestConfig()
        config._private_attr = "private_value"
        config.__mangled_attr = "mangled_value"

        # 私有属性应该可以直接通过 __getattribute__ 访问
        assert config._private_attr == "private_value"
        assert config.__mangled_attr == "mangled_value"

        # 但不会出现在配置中（不会被保存）
        config.save_to_json()
        with config._config_file.open("r", encoding="utf-8") as f:
            data = json.load(f)
        assert "_private_attr" not in data
        assert "__mangled_attr" not in data

    def test_dunder_methods_not_intercepted(self, temp_config_dir):
        """测试双下划线方法不被拦截."""

        class TestConfig(ConfigMixin):
            NAME = "dunder_test"

        config = TestConfig()

        # 特殊方法应该正常工作
        assert hasattr(config, "__class__")
        assert hasattr(config, "__dict__")
        assert str(type(config)).startswith("<class")


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