"""
config.py OSS 配置字段单元测试

覆盖：
- OSS 相关字段默认值
- 环境变量覆盖
- 字段名与代码引用一致
- pydantic_settings 加载行为
"""

from __future__ import annotations

import importlib.util
import os
from pathlib import Path
from unittest.mock import patch


def _load_settings_class():
    """
    直接加载 config.py 模块，绕过 apps/api/__init__.py 的副作用。
    apps/api/__init__.py 会导入 main.py，而 main.py 依赖 app 模块。
    """
    config_path = Path(__file__).resolve().parents[2] / "apps" / "api" / "app" / "config.py"
    spec = importlib.util.spec_from_file_location("config_module", config_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module.Settings


def _fresh_settings(**env_overrides: dict[str, str]):
    """
    每次创建一个全新的 Settings 实例。
    env_overrides 会注入到 os.environ。
    """
    env = {
        "JWT_SECRET_KEY": "unit-test-secret-key-12345",
        **env_overrides,
    }
    with patch.dict(os.environ, env, clear=True):
        Settings = _load_settings_class()
        return Settings()


# ---------------------------------------------------------------------------
# 默认值测试
# ---------------------------------------------------------------------------


class TestOSSConfigDefaults:
    """OSS 配置字段默认值必须与代码引用一致。"""

    def test_oss_endpoint_default(self):
        settings = _fresh_settings()
        assert settings.OSS_ENDPOINT == "oss-cn-hangzhou.aliyuncs.com"

    def test_oss_access_key_id_default_empty(self):
        settings = _fresh_settings()
        assert settings.OSS_ACCESS_KEY_ID == ""

    def test_oss_access_key_secret_default_empty(self):
        settings = _fresh_settings()
        assert settings.OSS_ACCESS_KEY_SECRET == ""

    def test_oss_bucket_name_default(self):
        settings = _fresh_settings()
        assert settings.OSS_BUCKET_NAME == "xiaoxia-autocut"

    def test_oss_direct_upload_max_mb_default_is_2000(self):
        """PR #117 修复：默认值从 800 改为 2000。"""
        settings = _fresh_settings()
        assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 2000

    def test_oss_direct_upload_expire_seconds_default(self):
        settings = _fresh_settings()
        assert settings.OSS_DIRECT_UPLOAD_EXPIRE_SECONDS == 900


# ---------------------------------------------------------------------------
# 环境变量覆盖测试
# ---------------------------------------------------------------------------


class TestOSSConfigEnvOverride:
    """环境变量能正确覆盖 OSS 配置字段。"""

    def test_oss_endpoint_override(self):
        settings = _fresh_settings(OSS_ENDPOINT="oss-cn-shanghai.aliyuncs.com")
        assert settings.OSS_ENDPOINT == "oss-cn-shanghai.aliyuncs.com"

    def test_oss_access_key_id_override(self):
        settings = _fresh_settings(OSS_ACCESS_KEY_ID="test-key-id")
        assert settings.OSS_ACCESS_KEY_ID == "test-key-id"

    def test_oss_access_key_secret_override(self):
        settings = _fresh_settings(OSS_ACCESS_KEY_SECRET="test-key-secret")
        assert settings.OSS_ACCESS_KEY_SECRET == "test-key-secret"

    def test_oss_bucket_name_override(self):
        settings = _fresh_settings(OSS_BUCKET_NAME="test-bucket")
        assert settings.OSS_BUCKET_NAME == "test-bucket"

    def test_oss_direct_upload_max_mb_override(self):
        settings = _fresh_settings(OSS_DIRECT_UPLOAD_MAX_MB="4096")
        assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 4096

    def test_oss_direct_upload_expire_seconds_override(self):
        settings = _fresh_settings(OSS_DIRECT_UPLOAD_EXPIRE_SECONDS="1800")
        assert settings.OSS_DIRECT_UPLOAD_EXPIRE_SECONDS == 1800


# ---------------------------------------------------------------------------
# 字段名一致性测试（防止再次出现字段名拼写错误导致 500）
# ---------------------------------------------------------------------------


class TestOSSConfigFieldNameConsistency:
    """
    确保 Settings 类包含代码中实际引用的所有字段。
    防止类似 OSS_DIRECT_UPLOAD_EXPRESS_SECRET 的拼写错误再次发生。
    """

    def test_settings_has_oss_endpoint_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_ENDPOINT")

    def test_settings_has_oss_access_key_id_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_ACCESS_KEY_ID")

    def test_settings_has_oss_access_key_secret_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_ACCESS_KEY_SECRET")

    def test_settings_has_oss_bucket_name_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_BUCKET_NAME")

    def test_settings_has_oss_direct_upload_max_mb_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_DIRECT_UPLOAD_MAX_MB")

    def test_settings_has_oss_direct_upload_expire_seconds_field(self):
        settings = _fresh_settings()
        assert hasattr(settings, "OSS_DIRECT_UPLOAD_EXPIRE_SECONDS")

    def test_property_aliases_match_field_names(self):
        """确保 property 访问器与字段值一致。"""
        settings = _fresh_settings(
            OSS_ENDPOINT="ep",
            OSS_ACCESS_KEY_ID="kid",
            OSS_ACCESS_KEY_SECRET="ksec",
            OSS_BUCKET_NAME="bkt",
        )
        assert settings.oss_endpoint == "ep"
        assert settings.oss_access_key_id == "kid"
        assert settings.oss_access_key_secret == "ksec"
        assert settings.oss_bucket_name == "bkt"


# ---------------------------------------------------------------------------
# extra="ignore" 行为测试
# ---------------------------------------------------------------------------


class TestSettingsExtraIgnore:
    """extra="ignore" 确保未知环境变量不会导致启动失败。"""

    def test_unknown_env_var_is_ignored(self):
        settings = _fresh_settings(UNKNOWN_RANDOM_VAR="whatever")
        assert not hasattr(settings, "UNKNOWN_RANDOM_VAR")

    def test_settings_loads_without_error(self):
        settings = _fresh_settings()
        assert settings.APP_NAME == "xiaoxia-saas"


# ---------------------------------------------------------------------------
# 别名测试（MAX_UPLOAD_SIZE_MB 兼容旧配置）
# ---------------------------------------------------------------------------


class TestOSSConfigAliases:
    """OSS_DIRECT_UPLOAD_MAX_MB 支持 MAX_UPLOAD_SIZE_MB 别名。"""

    def test_max_upload_size_mb_alias_works(self):
        """旧环境变量 MAX_UPLOAD_SIZE_MB 仍能生效。"""
        env = {
            "JWT_SECRET_KEY": "unit-test-secret-key-12345",
            "MAX_UPLOAD_SIZE_MB": "3000",
        }
        with patch.dict(os.environ, env, clear=False):
            os.environ.pop("OSS_DIRECT_UPLOAD_MAX_MB", None)
            Settings = _load_settings_class()
            settings = Settings()
            assert settings.OSS_DIRECT_UPLOAD_MAX_MB == 3000
