"""Tests for Pydantic v2 configuration models."""

import pytest
from pydantic import ValidationError

from llm_rotator.config import (
    ClientType,
    KeyConfig,
    ModelGroupConfig,
    ProviderConfig,
    RequestQuotaConfig,
    ResetSchedule,
    RotatorConfig,
    ServerErrorRetryConfig,
    TokenQuotaConfig,
)


def _minimal_provider(**overrides) -> dict:
    base = {
        "name": "openai",
        "client_type": "openai",
        "priority": 1,
        "models": ["gpt-4o"],
        "keys": [{"token": "sk-test", "alias": "main"}],
    }
    base.update(overrides)
    return base


def _minimal_config(**overrides) -> dict:
    base = {"providers": [_minimal_provider()]}
    base.update(overrides)
    return base


class TestClientType:
    def test_valid_values(self):
        assert ClientType("openai") == ClientType.OPENAI
        assert ClientType("gemini") == ClientType.GEMINI


class TestResetSchedule:
    def test_valid_values(self):
        assert ResetSchedule("daily_utc") == ResetSchedule.DAILY_UTC
        assert ResetSchedule("rolling_window") == ResetSchedule.ROLLING_WINDOW


class TestKeyConfig:
    def test_minimal(self):
        key = KeyConfig(token="sk-123", alias="main")
        assert key.token == "sk-123"
        assert key.alias == "main"
        assert key.request_quota is None

    def test_with_request_quota(self):
        key = KeyConfig(
            token="sk-123",
            alias="main",
            request_quota={"limit": 40, "reset": "daily_utc"},
        )
        assert key.request_quota.limit == 40


class TestTokenQuotaConfig:
    def test_valid(self):
        q = TokenQuotaConfig(limit=250_000, reset="daily_utc")
        assert q.limit == 250_000
        assert q.reset == ResetSchedule.DAILY_UTC

    def test_rolling_window_requires_window_seconds(self):
        with pytest.raises(ValidationError):
            TokenQuotaConfig(limit=100, reset="rolling_window")

    def test_rolling_window_with_window_seconds(self):
        q = TokenQuotaConfig(limit=100, reset="rolling_window", window_seconds=3600)
        assert q.window_seconds == 3600


class TestRequestQuotaConfig:
    def test_valid(self):
        q = RequestQuotaConfig(limit=40, reset="daily_utc")
        assert q.limit == 40

    def test_rolling_window_requires_window_seconds(self):
        with pytest.raises(ValidationError):
            RequestQuotaConfig(limit=10, reset="rolling_window")


class TestModelGroupConfig:
    def test_valid(self):
        g = ModelGroupConfig(name="flagship", tier=1, models=["gpt-4o", "gpt-5"])
        assert g.tier == 1
        assert g.models == ["gpt-4o", "gpt-5"]

    def test_tier_must_be_positive(self):
        with pytest.raises(ValidationError):
            ModelGroupConfig(name="bad", tier=0, models=["m"])

    def test_tier_negative_fails(self):
        with pytest.raises(ValidationError):
            ModelGroupConfig(name="bad", tier=-1, models=["m"])

    def test_empty_models_fails(self):
        with pytest.raises(ValidationError):
            ModelGroupConfig(name="g", tier=1, models=[])

    def test_with_token_quota(self):
        g = ModelGroupConfig(
            name="flagship",
            tier=1,
            models=["gpt-4o"],
            token_quota={"limit": 250_000, "reset": "daily_utc"},
        )
        assert g.token_quota.limit == 250_000


class TestServerErrorRetryConfig:
    def test_defaults(self):
        c = ServerErrorRetryConfig()
        assert c.max_attempts == 1
        assert c.delay_seconds == 0.5

    def test_custom(self):
        c = ServerErrorRetryConfig(max_attempts=0, delay_seconds=0.0)
        assert c.max_attempts == 0


class TestProviderConfig:
    def test_minimal_with_models(self):
        p = ProviderConfig(**_minimal_provider())
        assert p.name == "openai"
        assert p.models == ["gpt-4o"]
        assert p.model_groups is None

    def test_with_model_groups(self):
        p = ProviderConfig(
            **_minimal_provider(
                models=None,
                model_groups=[
                    {"name": "flagship", "tier": 1, "models": ["gpt-5", "gpt-4o"]},
                    {"name": "mini", "tier": 3, "models": ["gpt-4o-mini"]},
                ],
            )
        )
        assert len(p.model_groups) == 2
        assert p.models is None

    def test_models_xor_model_groups(self):
        """Cannot specify both models and model_groups."""
        with pytest.raises(ValidationError, match=r"models.*model_groups"):
            ProviderConfig(
                **_minimal_provider(
                    models=["gpt-4o"],
                    model_groups=[
                        {"name": "g", "tier": 1, "models": ["gpt-4o"]},
                    ],
                )
            )

    def test_neither_models_nor_model_groups_fails(self):
        with pytest.raises(ValidationError):
            ProviderConfig(
                **_minimal_provider(models=None, model_groups=None)
            )

    def test_empty_keys_fails(self):
        with pytest.raises(ValidationError):
            ProviderConfig(**_minimal_provider(keys=[]))

    def test_server_error_retry_defaults(self):
        p = ProviderConfig(**_minimal_provider())
        assert p.server_error_retry.max_attempts == 1

    def test_base_url_default_openai(self):
        p = ProviderConfig(**_minimal_provider())
        assert p.base_url == "https://api.openai.com/v1"

    def test_base_url_custom(self):
        p = ProviderConfig(**_minimal_provider(base_url="https://openrouter.ai/api/v1"))
        assert p.base_url == "https://openrouter.ai/api/v1"

    def test_gemini_no_default_base_url(self):
        p = ProviderConfig(
            **_minimal_provider(client_type="gemini", base_url=None)
        )
        assert p.base_url is None


class TestRotatorConfig:
    def test_minimal(self):
        cfg = RotatorConfig(**_minimal_config())
        assert len(cfg.providers) == 1

    def test_providers_sorted_by_priority(self):
        cfg = RotatorConfig(
            providers=[
                _minimal_provider(name="low", priority=3),
                _minimal_provider(name="high", priority=1),
                _minimal_provider(name="mid", priority=2),
            ]
        )
        assert [p.name for p in cfg.providers] == ["high", "mid", "low"]

    def test_duplicate_priorities_fails(self):
        with pytest.raises(ValidationError, match="priority"):
            RotatorConfig(
                providers=[
                    _minimal_provider(name="a", priority=1),
                    _minimal_provider(name="b", priority=1),
                ]
            )

    def test_empty_providers_fails(self):
        with pytest.raises(ValidationError):
            RotatorConfig(providers=[])

    def test_from_dict(self):
        """Config can be built from plain dict."""
        cfg = RotatorConfig.model_validate(_minimal_config())
        assert cfg.providers[0].name == "openai"
