"""Tests for web/utils/rate_limiter.py."""

import pytest
from flask import Flask


@pytest.fixture
def app():
    """Create test Flask application."""
    app = Flask(__name__)
    app.config["TESTING"] = True
    return app


class TestGetClientIp:
    """Tests for get_client_ip function."""

    def test_returns_first_ip_from_x_forwarded_for(self, app):
        """Test that first IP from X-Forwarded-For chain is returned."""
        with app.test_request_context(
            environ_base={
                "HTTP_X_FORWARDED_FOR": "192.168.1.100, 10.0.0.1, 172.16.0.1"
            }
        ):
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            result = get_client_ip()
            assert result == "192.168.1.100"

    def test_strips_whitespace_from_forwarded_ip(self, app):
        """Test that whitespace is stripped from forwarded IP."""
        with app.test_request_context(
            environ_base={"HTTP_X_FORWARDED_FOR": "  192.168.1.100  , 10.0.0.1"}
        ):
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            result = get_client_ip()
            assert result == "192.168.1.100"

    def test_returns_x_real_ip_when_no_forwarded_for(self, app):
        """Test that X-Real-IP is used when X-Forwarded-For is absent."""
        with app.test_request_context(
            environ_base={"HTTP_X_REAL_IP": "10.20.30.40"}
        ):
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            result = get_client_ip()
            assert result == "10.20.30.40"

    def test_strips_whitespace_from_real_ip(self, app):
        """Test that whitespace is stripped from X-Real-IP."""
        with app.test_request_context(
            environ_base={"HTTP_X_REAL_IP": "  10.20.30.40  "}
        ):
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            result = get_client_ip()
            assert result == "10.20.30.40"

    def test_falls_back_to_remote_address(self, app):
        """Test fallback to get_remote_address when no proxy headers."""
        with app.test_request_context():
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            # When no proxy headers are set, get_remote_address returns the remote addr
            result = get_client_ip()
            # Result should be some IP (default is typically 127.0.0.1)
            assert result is not None

    def test_prefers_x_forwarded_for_over_x_real_ip(self, app):
        """Test that X-Forwarded-For takes precedence over X-Real-IP."""
        with app.test_request_context(
            environ_base={
                "HTTP_X_FORWARDED_FOR": "192.168.1.1",
                "HTTP_X_REAL_IP": "10.0.0.1",
            }
        ):
            from local_deep_research.web.utils.rate_limiter import get_client_ip

            result = get_client_ip()
            assert result == "192.168.1.1"


class TestRateLimiterConfiguration:
    """Tests for rate limiter configuration."""

    def test_limiter_uses_get_client_ip_as_key_func(self):
        """Test that limiter is configured with get_client_ip as key function."""
        from local_deep_research.web.utils.rate_limiter import (
            limiter,
            get_client_ip,
        )

        assert limiter._key_func == get_client_ip

    def test_limiter_uses_memory_storage(self):
        """Test that limiter uses in-memory storage by default."""
        from local_deep_research.web.utils.rate_limiter import limiter

        # The storage URI should be memory
        assert limiter._storage_uri == "memory://"

    def test_limiter_has_headers_enabled(self):
        """Test that rate limit headers are enabled."""
        from local_deep_research.web.utils.rate_limiter import limiter

        assert limiter._headers_enabled is True

    def test_login_limit_exists(self):
        """Test that login_limit is defined."""
        from local_deep_research.web.utils.rate_limiter import login_limit

        assert login_limit is not None

    def test_registration_limit_exists(self):
        """Test that registration_limit is defined."""
        from local_deep_research.web.utils.rate_limiter import (
            registration_limit,
        )

        assert registration_limit is not None


class TestRateLimitConstants:
    """Tests for rate limit configuration constants."""

    def test_default_rate_limit_loaded_from_config(self):
        """Test that DEFAULT_RATE_LIMIT is loaded from server config."""
        from local_deep_research.web.utils.rate_limiter import (
            DEFAULT_RATE_LIMIT,
        )

        # Should be a string like "X per hour" or similar
        assert isinstance(DEFAULT_RATE_LIMIT, str)
        assert DEFAULT_RATE_LIMIT  # Not empty

    def test_login_rate_limit_loaded_from_config(self):
        """Test that LOGIN_RATE_LIMIT is loaded from server config."""
        from local_deep_research.web.utils.rate_limiter import LOGIN_RATE_LIMIT

        assert isinstance(LOGIN_RATE_LIMIT, str)
        assert LOGIN_RATE_LIMIT  # Not empty

    def test_registration_rate_limit_loaded_from_config(self):
        """Test that REGISTRATION_RATE_LIMIT is loaded from server config."""
        from local_deep_research.web.utils.rate_limiter import (
            REGISTRATION_RATE_LIMIT,
        )

        assert isinstance(REGISTRATION_RATE_LIMIT, str)
        assert REGISTRATION_RATE_LIMIT  # Not empty

    def test_settings_rate_limit_loaded_from_config(self):
        """Test that SETTINGS_RATE_LIMIT is loaded from config with default fallback."""
        from local_deep_research.web.utils.rate_limiter import (
            SETTINGS_RATE_LIMIT,
        )

        assert isinstance(SETTINGS_RATE_LIMIT, str)
        assert SETTINGS_RATE_LIMIT  # Not empty
        # Should contain a rate expression like "30 per minute"
        assert "per" in SETTINGS_RATE_LIMIT


class TestSettingsLimit:
    """Tests for settings_limit shared rate limiter (PR #2021)."""

    def test_settings_limit_exists(self):
        """Test that settings_limit is defined."""
        from local_deep_research.web.utils.rate_limiter import settings_limit

        assert settings_limit is not None

    def test_settings_limit_is_shared_limit(self):
        """Test that settings_limit is a SharedLimitItem from flask-limiter."""
        from local_deep_research.web.utils.rate_limiter import settings_limit

        # SharedLimitItem has a __call__ method (it's a decorator)
        assert callable(settings_limit)

    def test_settings_limit_can_decorate_function(self):
        """Test that settings_limit can be used as a decorator."""
        from local_deep_research.web.utils.rate_limiter import settings_limit

        @settings_limit
        def dummy_view():
            return "ok"

        # The decorated function should still be callable
        assert callable(dummy_view)

    def test_settings_limit_default_value(self):
        """Test that SETTINGS_RATE_LIMIT defaults to 30 per minute."""
        from local_deep_research.web.utils.rate_limiter import (
            SETTINGS_RATE_LIMIT,
        )

        # The config.get uses "30 per minute" as default
        # If not set in config, it should be "30 per minute"
        assert isinstance(SETTINGS_RATE_LIMIT, str)
        assert SETTINGS_RATE_LIMIT  # Not empty
