"""Tests for HTTPConfig."""

import pytest

from pinecone._internal.constants import DEFAULT_TIMEOUT
from pinecone._internal.http.config import HTTPConfig


pytestmark = pytest.mark.unit


class TestHTTPConfigTimeout:
    """Test HTTPConfig timeout validation."""

    def test_default_timeout(self):
        """Test default timeout value uses DEFAULT_TIMEOUT constant."""
        config = HTTPConfig()
        assert config.timeout == DEFAULT_TIMEOUT

    def test_default_timeout_uses_constant(self):
        """Test that default timeout uses DEFAULT_TIMEOUT constant.

        This ensures single source of truth: if DEFAULT_TIMEOUT changes,
        HTTPConfig default automatically uses the new value.
        """
        config = HTTPConfig()
        assert config.timeout == DEFAULT_TIMEOUT

    def test_custom_int_timeout(self):
        """Test custom integer timeout."""
        config = HTTPConfig(timeout=60)
        assert config.timeout == 60

    def test_custom_float_timeout(self):
        """Test custom float timeout."""
        config = HTTPConfig(timeout=45.5)
        assert config.timeout == 45.5

    def test_granular_timeout_dict(self):
        """Test granular timeout configuration with dict."""
        timeout = {"connect": 5, "read": 120, "write": 30, "pool": 10}
        config = HTTPConfig(timeout=timeout)
        assert config.timeout == timeout

    def test_partial_granular_timeout_dict(self):
        """Test partial granular timeout configuration."""
        timeout = {"connect": 5, "read": 60}
        config = HTTPConfig(timeout=timeout)
        assert config.timeout == timeout

    def test_zero_timeout_raises_error(self):
        """Test that zero timeout raises ValueError."""
        with pytest.raises(ValueError, match="timeout must be positive"):
            HTTPConfig(timeout=0)

    def test_negative_timeout_raises_error(self):
        """Test that negative timeout raises ValueError."""
        with pytest.raises(ValueError, match="timeout must be positive"):
            HTTPConfig(timeout=-5)

    def test_invalid_timeout_dict_key_raises_error(self):
        """Test that invalid timeout dict keys raise ValueError."""
        with pytest.raises(ValueError, match="Invalid timeout keys"):
            HTTPConfig(timeout={"connect": 5, "invalid_key": 10})

    def test_negative_timeout_in_dict_raises_error(self):
        """Test that negative timeout values in dict raise ValueError."""
        with pytest.raises(ValueError, match=r"timeout.connect must be positive"):
            HTTPConfig(timeout={"connect": -5, "read": 60})

    def test_zero_timeout_in_dict_raises_error(self):
        """Test that zero timeout values in dict raise ValueError."""
        with pytest.raises(ValueError, match=r"timeout.read must be positive"):
            HTTPConfig(timeout={"connect": 5, "read": 0})

    def test_all_granular_timeout_keys(self):
        """Test that all valid granular timeout keys work."""
        timeout = {
            "connect": 5.0,
            "read": 60.0,
            "write": 30.0,
            "pool": 10.0,
        }
        config = HTTPConfig(timeout=timeout)
        assert config.timeout == timeout

    def test_invalid_timeout_type_raises_type_error(self):
        """Test that invalid timeout type raises TypeError."""
        with pytest.raises(TypeError, match="timeout must be int, float, or dict"):
            HTTPConfig(timeout="invalid")  # type: ignore[arg-type]

    def test_invalid_timeout_type_list_raises_type_error(self):
        """Test that list timeout type raises TypeError."""
        with pytest.raises(TypeError, match="timeout must be int, float, or dict"):
            HTTPConfig(timeout=[30, 60])  # type: ignore[arg-type]


class TestHTTPConfigValidation:
    """Test HTTPConfig validation errors."""

    def test_negative_max_retries_raises_error(self):
        """Test that negative max_retries raises ValueError."""
        with pytest.raises(ValueError, match="max_retries must be non-negative"):
            HTTPConfig(max_retries=-1)

    def test_zero_max_connections_raises_error(self):
        """Test that zero max_connections raises ValueError."""
        with pytest.raises(ValueError, match="max_connections must be positive"):
            HTTPConfig(max_connections=0)

    def test_negative_max_connections_raises_error(self):
        """Test that negative max_connections raises ValueError."""
        with pytest.raises(ValueError, match="max_connections must be positive"):
            HTTPConfig(max_connections=-5)

    def test_negative_max_keepalive_connections_raises_error(self):
        """Test that negative max_keepalive_connections raises ValueError."""
        with pytest.raises(ValueError, match="max_keepalive_connections must be non-negative"):
            HTTPConfig(max_keepalive_connections=-1)

    def test_keepalive_exceeds_max_connections_raises_error(self):
        """Test that max_keepalive_connections > max_connections raises ValueError."""
        with pytest.raises(ValueError, match=r"max_keepalive_connections.*cannot exceed"):
            HTTPConfig(max_connections=10, max_keepalive_connections=20)

    def test_zero_retry_backoff_factor_raises_error(self):
        """Test that zero retry_backoff_factor raises ValueError."""
        with pytest.raises(ValueError, match="retry_backoff_factor must be positive"):
            HTTPConfig(retry_backoff_factor=0)

    def test_negative_retry_backoff_factor_raises_error(self):
        """Test that negative retry_backoff_factor raises ValueError."""
        with pytest.raises(ValueError, match="retry_backoff_factor must be positive"):
            HTTPConfig(retry_backoff_factor=-1.5)

    def test_zero_retry_max_wait_raises_error(self):
        """Test that zero retry_max_wait raises ValueError."""
        with pytest.raises(ValueError, match="retry_max_wait must be positive"):
            HTTPConfig(retry_max_wait=0)

    def test_negative_retry_max_wait_raises_error(self):
        """Test that negative retry_max_wait raises ValueError."""
        with pytest.raises(ValueError, match="retry_max_wait must be positive"):
            HTTPConfig(retry_max_wait=-10)

    def test_rate_limit_enabled_with_zero_tokens_raises_error(self):
        """Test that rate_limit_enabled=True with zero tokens raises ValueError."""
        with pytest.raises(ValueError, match="rate_limit_tokens must be positive"):
            HTTPConfig(rate_limit_enabled=True, rate_limit_tokens=0)

    def test_rate_limit_enabled_with_negative_tokens_raises_error(self):
        """Test that rate_limit_enabled=True with negative tokens raises ValueError."""
        with pytest.raises(ValueError, match="rate_limit_tokens must be positive"):
            HTTPConfig(rate_limit_enabled=True, rate_limit_tokens=-5)

    def test_rate_limit_enabled_with_zero_period_raises_error(self):
        """Test that rate_limit_enabled=True with zero period raises ValueError."""
        with pytest.raises(ValueError, match="rate_limit_period must be positive"):
            HTTPConfig(rate_limit_enabled=True, rate_limit_period=0.0)

    def test_rate_limit_enabled_with_negative_period_raises_error(self):
        """Test that rate_limit_enabled=True with negative period raises ValueError."""
        with pytest.raises(ValueError, match="rate_limit_period must be positive"):
            HTTPConfig(rate_limit_enabled=True, rate_limit_period=-10.0)

    def test_empty_retry_statuses_raises_error(self):
        """Test that empty retry_statuses raises ValueError."""
        with pytest.raises(ValueError, match="retry_statuses cannot be empty"):
            HTTPConfig(retry_statuses=())

    def test_rate_limit_config_valid(self):
        """Test that valid rate limit configuration works."""
        config = HTTPConfig(rate_limit_enabled=True, rate_limit_tokens=100, rate_limit_period=60.0)
        assert config.rate_limit_enabled is True
        assert config.rate_limit_tokens == 100
        assert config.rate_limit_period == 60.0
