"""
Tests for AdaptiveRateLimitTracker.
"""

from unittest.mock import patch

from local_deep_research.web_search_engines.rate_limiting.tracker import (
    AdaptiveRateLimitTracker,
)


class TestAdaptiveRateLimitTrackerInit:
    """Tests for AdaptiveRateLimitTracker initialization."""

    def test_init_with_default_settings(self):
        """Initializes with default settings."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)

            # Should use defaults when settings are not available
            assert tracker.memory_window == 100
            assert tracker.exploration_rate == 0.1
            assert tracker.learning_rate == 0.3
            assert tracker.decay_per_day == 0.95

    def test_init_programmatic_mode(self):
        """Initializes in programmatic mode."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert tracker.programmatic_mode is True

    def test_init_with_settings_snapshot(self, mock_settings_snapshot):
        """Initializes with settings snapshot."""
        tracker = AdaptiveRateLimitTracker(
            settings_snapshot=mock_settings_snapshot,
            programmatic_mode=True,
        )
        assert tracker.settings_snapshot == mock_settings_snapshot

    def test_init_empty_caches(self):
        """Initializes with empty caches."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert tracker.recent_attempts == {}
        assert tracker.current_estimates == {}


class TestAdaptiveRateLimitTrackerProfiles:
    """Tests for rate limiting profiles."""

    def test_balanced_profile(self):
        """Balanced profile keeps default values."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
            tracker._apply_profile("balanced")

            # Values should be unchanged from defaults
            assert tracker.exploration_rate == 0.1
            assert tracker.learning_rate == 0.3

    def test_conservative_profile(self):
        """Conservative profile reduces exploration and learning rates."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
            original_exploration = tracker.exploration_rate
            original_learning = tracker.learning_rate

            tracker._apply_profile("conservative")

            # Values should be reduced
            assert tracker.exploration_rate < original_exploration
            assert tracker.learning_rate < original_learning

    def test_aggressive_profile(self):
        """Aggressive profile increases exploration and learning rates."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
            original_exploration = tracker.exploration_rate
            original_learning = tracker.learning_rate

            tracker._apply_profile("aggressive")

            # Values should be increased
            assert tracker.exploration_rate > original_exploration
            assert tracker.learning_rate > original_learning


class TestAdaptiveRateLimitTrackerEnabled:
    """Tests for enabled/disabled state."""

    def test_enabled_by_default_in_normal_mode(self):
        """Enabled by default in normal mode."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=False)
            assert tracker.enabled is True

    def test_disabled_by_default_in_programmatic_mode(self):
        """Disabled by default in programmatic mode."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
            assert tracker.enabled is False


class TestAdaptiveRateLimitTrackerMemory:
    """Tests for in-memory tracking."""

    def test_recent_attempts_stored(self):
        """Recent attempts are stored in memory."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)

        # Manually add some attempts
        from collections import deque

        tracker.recent_attempts["test_engine"] = deque(maxlen=100)
        tracker.recent_attempts["test_engine"].append(
            {
                "wait_time": 1.0,
                "success": True,
                "timestamp": 1234567890,
            }
        )

        assert len(tracker.recent_attempts["test_engine"]) == 1

    def test_estimates_stored(self):
        """Estimates are stored in memory."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)

        tracker.current_estimates["test_engine"] = {
            "min_wait": 0.5,
            "max_wait": 2.0,
            "avg_wait": 1.0,
        }

        assert "test_engine" in tracker.current_estimates
        assert tracker.current_estimates["test_engine"]["avg_wait"] == 1.0


class TestAdaptiveRateLimitTrackerDatabaseSkip:
    """Tests for database operation skipping."""

    def test_load_estimates_skipped_in_programmatic_mode(self):
        """_load_estimates skips database in programmatic mode."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker._get_db_imports"
        ):
            tracker = AdaptiveRateLimitTracker(programmatic_mode=True)

            # Should not attempt to get database imports
            assert tracker._estimates_loaded is True

    def test_load_estimates_deferred_in_non_programmatic_mode(self):
        """_load_estimates defers loading in non-programmatic mode."""
        with patch(
            "local_deep_research.web_search_engines.rate_limiting.tracker.get_setting_from_snapshot"
        ) as mock_get:
            from local_deep_research.config.thread_settings import (
                NoSettingsContextError,
            )

            mock_get.side_effect = NoSettingsContextError()

            tracker = AdaptiveRateLimitTracker(programmatic_mode=False)

            # Non-programmatic defers loading until user context is available
            assert tracker._estimates_loaded is False
            assert tracker.current_estimates == {}


class TestRateLimitTrackerConstants:
    """Tests for tracker constants and settings."""

    def test_memory_window_positive(self):
        """Memory window is positive."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert tracker.memory_window > 0

    def test_exploration_rate_valid_range(self):
        """Exploration rate is between 0 and 1."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert 0 <= tracker.exploration_rate <= 1

    def test_learning_rate_valid_range(self):
        """Learning rate is between 0 and 1."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert 0 <= tracker.learning_rate <= 1

    def test_decay_per_day_valid_range(self):
        """Decay per day is between 0 and 1."""
        tracker = AdaptiveRateLimitTracker(programmatic_mode=True)
        assert 0 <= tracker.decay_per_day <= 1
