"""Configuration boundary tests for the simplified v1 API."""

from __future__ import annotations

import importlib

import pytest

import pollux.config as config_module
from pollux.config import Config
from pollux.errors import ConfigurationError

pytestmark = pytest.mark.unit


def test_config_creation_with_mock_mode(gemini_model: str) -> None:
    """Config can be created with mock mode (no API key needed)."""
    cfg = Config(provider="gemini", model=gemini_model, use_mock=True)
    assert cfg.provider == "gemini"
    assert cfg.model == gemini_model


def test_config_auto_resolves_api_key_from_env(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """API key should be auto-resolved from environment."""
    monkeypatch.setenv("GEMINI_API_KEY", "env-key")

    cfg = Config(provider="gemini", model=gemini_model)

    assert cfg.api_key == "env-key"


def test_config_module_reload_has_no_dotenv_import_side_effect(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """Reloading config module should not trigger dotenv loading."""
    calls = 0

    def fake_load_dotenv(*_args: object, **_kwargs: object) -> bool:
        nonlocal calls
        calls += 1
        return False

    monkeypatch.setattr("dotenv.load_dotenv", fake_load_dotenv)

    importlib.reload(config_module)

    assert calls == 0


def test_config_loads_dotenv_lazily_when_env_missing(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """When env is missing, Config should trigger dotenv loading at init time."""
    monkeypatch.delenv("GEMINI_API_KEY", raising=False)

    calls = 0

    def fake_load_dotenv() -> bool:
        nonlocal calls
        calls += 1
        monkeypatch.setenv("GEMINI_API_KEY", "dotenv-key")
        return True

    monkeypatch.setattr("pollux.config.dotenv.load_dotenv", fake_load_dotenv)

    cfg = Config(provider="gemini", model=gemini_model)

    assert calls == 1
    assert cfg.api_key == "dotenv-key"


def test_config_skips_dotenv_when_env_key_already_present(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """If the key is already exported, dotenv loading is unnecessary."""
    monkeypatch.setenv("GEMINI_API_KEY", "env-key")

    calls = 0

    def fake_load_dotenv() -> bool:
        nonlocal calls
        calls += 1
        return True

    monkeypatch.setattr("pollux.config.dotenv.load_dotenv", fake_load_dotenv)

    cfg = Config(provider="gemini", model=gemini_model)

    assert calls == 0
    assert cfg.api_key == "env-key"


def test_explicit_api_key_takes_precedence(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """Explicit api_key should override env."""
    monkeypatch.setenv("GEMINI_API_KEY", "env-key")

    cfg = Config(provider="gemini", model=gemini_model, api_key="explicit-key")

    assert cfg.api_key == "explicit-key"


def test_openai_provider_uses_openai_api_key(
    monkeypatch: pytest.MonkeyPatch,
    openai_model: str,
) -> None:
    """Provider-specific env key selection should prefer OPENAI_API_KEY."""
    monkeypatch.setenv("OPENAI_API_KEY", "openai-secret")
    monkeypatch.delenv("GEMINI_API_KEY", raising=False)

    cfg = Config(provider="openai", model=openai_model)

    assert cfg.provider == "openai"
    assert cfg.api_key == "openai-secret"


def test_missing_api_key_raises_clear_error(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """Missing API key without mock mode must fail clearly."""
    monkeypatch.delenv("GEMINI_API_KEY", raising=False)

    with pytest.raises(ConfigurationError, match="API key required") as exc:
        Config(provider="gemini", model=gemini_model)
    assert exc.value.hint is not None
    assert "GEMINI_API_KEY" in exc.value.hint


def test_mock_mode_does_not_require_api_key(
    monkeypatch: pytest.MonkeyPatch,
    gemini_model: str,
) -> None:
    """Mock mode should work without an API key."""
    monkeypatch.delenv("GEMINI_API_KEY", raising=False)

    cfg = Config(provider="gemini", model=gemini_model, use_mock=True)

    assert cfg.use_mock is True
    assert cfg.api_key is None


def test_unknown_provider_raises_error() -> None:
    """Unknown provider should raise a clear error."""
    with pytest.raises(ConfigurationError, match="Unknown provider"):
        Config(provider="unknown", model="some-model", use_mock=True)  # type: ignore[arg-type]


def test_zero_request_concurrency_raises_clear_error(gemini_model: str) -> None:
    """request_concurrency=0 would hang the pipeline; must fail at construction."""
    with pytest.raises(ConfigurationError, match="request_concurrency must be") as exc:
        Config(
            provider="gemini", model=gemini_model, use_mock=True, request_concurrency=0
        )
    assert exc.value.hint is not None
    assert "parallel" in exc.value.hint


def test_negative_request_concurrency_raises_clear_error(gemini_model: str) -> None:
    """Negative concurrency is always a caller mistake."""
    with pytest.raises(ConfigurationError, match="request_concurrency must be") as exc:
        Config(
            provider="gemini", model=gemini_model, use_mock=True, request_concurrency=-1
        )
    assert exc.value.hint is not None


def test_non_integer_request_concurrency_raises_clear_error(gemini_model: str) -> None:
    """Non-integer concurrency should raise ConfigurationError, not TypeError."""
    with pytest.raises(ConfigurationError, match="must be an integer") as exc:
        Config(
            provider="gemini",
            model=gemini_model,
            use_mock=True,
            request_concurrency="2",  # type: ignore[arg-type]
        )
    assert exc.value.hint is not None


def test_negative_ttl_seconds_raises_clear_error(gemini_model: str) -> None:
    """Negative TTL is nonsensical; must fail at construction."""
    with pytest.raises(ConfigurationError, match="ttl_seconds must be") as exc:
        Config(provider="gemini", model=gemini_model, use_mock=True, ttl_seconds=-1)
    assert exc.value.hint is not None


def test_non_integer_ttl_seconds_raises_clear_error(gemini_model: str) -> None:
    """Non-integer TTL should raise ConfigurationError, not TypeError."""
    with pytest.raises(ConfigurationError, match="must be an integer") as exc:
        Config(
            provider="gemini",
            model=gemini_model,
            use_mock=True,
            ttl_seconds="3600",  # type: ignore[arg-type]
        )
    assert exc.value.hint is not None


def test_zero_ttl_seconds_is_allowed(gemini_model: str) -> None:
    """ttl_seconds=0 is valid (disables caching TTL)."""
    cfg = Config(provider="gemini", model=gemini_model, use_mock=True, ttl_seconds=0)
    assert cfg.ttl_seconds == 0


def test_config_str_and_repr_redact_api_key(gemini_model: str) -> None:
    """String representations must not leak secrets."""
    secret = "top-secret-key"
    cfg = Config(provider="gemini", model=gemini_model, api_key=secret)

    assert secret not in str(cfg)
    assert secret not in repr(cfg)
    assert "[REDACTED]" in str(cfg)
