"""Tests for rate limiting and notification endpoints in settings_routes.py."""

from unittest.mock import patch, MagicMock
import time

SETTINGS_PREFIX = "/settings"


class TestApiGetRateLimitingStatus:
    """Tests for /api/rate-limiting/status endpoint."""

    def test_requires_authentication(self, client):
        response = client.get(f"{SETTINGS_PREFIX}/api/rate-limiting/status")
        assert response.status_code in [401, 302]

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_returns_status_and_engines(
        self, mock_get_tracker, authenticated_client
    ):
        mock_tracker = MagicMock()
        mock_tracker.enabled = True
        mock_tracker.exploration_rate = 0.1
        mock_tracker.learning_rate = 0.05
        mock_tracker.memory_window = 100
        mock_tracker.get_stats.return_value = [
            ("google", 2.0, 1.0, 5.0, time.time(), 50, 0.95),
            ("bing", 1.5, 0.5, 3.0, time.time(), 30, 0.8),
        ]
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/rate-limiting/status"
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["status"]["enabled"] is True
        assert len(data["engines"]) == 2
        assert data["engines"][0]["engine_type"] == "google"
        assert data["engines"][0]["success_rate"] == 95.0

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_handles_none_success_rate(
        self, mock_get_tracker, authenticated_client
    ):
        mock_tracker = MagicMock()
        mock_tracker.enabled = True
        mock_tracker.exploration_rate = 0.1
        mock_tracker.learning_rate = 0.05
        mock_tracker.memory_window = 100
        mock_tracker.get_stats.return_value = [
            ("new_engine", 1.0, 0.5, 2.0, time.time(), 0, None),
        ]
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/rate-limiting/status"
        )
        data = response.get_json()
        assert data["engines"][0]["success_rate"] == 0.0

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_error_returns_500(self, mock_get_tracker, authenticated_client):
        mock_get_tracker.side_effect = RuntimeError("tracker init failed")

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/rate-limiting/status"
        )
        assert response.status_code == 500
        data = response.get_json()
        assert "error" in data


class TestApiResetEngineRateLimiting:
    """Tests for /api/rate-limiting/engines/<engine_type>/reset endpoint."""

    def test_requires_authentication(self, client):
        response = client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/engines/google/reset"
        )
        assert response.status_code in [401, 302]

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_resets_engine(self, mock_get_tracker, authenticated_client):
        mock_tracker = MagicMock()
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/engines/google/reset"
        )
        assert response.status_code == 200
        data = response.get_json()
        assert "google" in data["message"]
        mock_tracker.reset_engine.assert_called_once_with("google")

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_error_returns_500(self, mock_get_tracker, authenticated_client):
        mock_tracker = MagicMock()
        mock_tracker.reset_engine.side_effect = RuntimeError("fail")
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/engines/google/reset"
        )
        assert response.status_code == 500


class TestApiCleanupRateLimiting:
    """Tests for /api/rate-limiting/cleanup endpoint."""

    def test_requires_authentication(self, client):
        response = client.post(f"{SETTINGS_PREFIX}/api/rate-limiting/cleanup")
        assert response.status_code in [401, 302]

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_cleanup_default_days(self, mock_get_tracker, authenticated_client):
        mock_tracker = MagicMock()
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/cleanup"
        )
        assert response.status_code == 200
        data = response.get_json()
        assert "30 days" in data["message"]
        mock_tracker.cleanup_old_data.assert_called_once_with(30)

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_cleanup_custom_days(self, mock_get_tracker, authenticated_client):
        mock_tracker = MagicMock()
        mock_get_tracker.return_value = mock_tracker

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/cleanup",
            json={"days": 7},
        )
        assert response.status_code == 200
        data = response.get_json()
        assert "7 days" in data["message"]
        mock_tracker.cleanup_old_data.assert_called_once_with(7)

    @patch("local_deep_research.web_search_engines.rate_limiting.get_tracker")
    def test_error_returns_500(self, mock_get_tracker, authenticated_client):
        mock_get_tracker.side_effect = RuntimeError("fail")

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/rate-limiting/cleanup"
        )
        assert response.status_code == 500


class TestCheckOllamaStatusSettings:
    """Tests for /api/ollama-status endpoint in settings_routes."""

    def test_requires_authentication(self, client):
        response = client.get(f"{SETTINGS_PREFIX}/api/ollama-status")
        assert response.status_code in [401, 302]

    @patch("local_deep_research.web.routes.settings_routes.safe_get")
    @patch(
        "local_deep_research.web.routes.settings_routes._get_setting_from_session"
    )
    def test_ollama_running(
        self, mock_get_setting, mock_safe_get, authenticated_client
    ):
        mock_get_setting.return_value = "http://localhost:11434"
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"version": "0.3.0"}
        mock_safe_get.return_value = mock_resp

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/ollama-status"
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["running"] is True
        assert data["version"] == "0.3.0"

    @patch("local_deep_research.web.routes.settings_routes.safe_get")
    @patch(
        "local_deep_research.web.routes.settings_routes._get_setting_from_session"
    )
    def test_ollama_non_200(
        self, mock_get_setting, mock_safe_get, authenticated_client
    ):
        mock_get_setting.return_value = "http://localhost:11434"
        mock_resp = MagicMock()
        mock_resp.status_code = 500
        mock_safe_get.return_value = mock_resp

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/ollama-status"
        )
        data = response.get_json()
        assert data["running"] is False

    @patch("local_deep_research.web.routes.settings_routes.safe_get")
    @patch(
        "local_deep_research.web.routes.settings_routes._get_setting_from_session"
    )
    def test_ollama_connection_error(
        self, mock_get_setting, mock_safe_get, authenticated_client
    ):
        import requests

        mock_get_setting.return_value = "http://localhost:11434"
        mock_safe_get.side_effect = requests.exceptions.ConnectionError(
            "refused"
        )

        response = authenticated_client.get(
            f"{SETTINGS_PREFIX}/api/ollama-status"
        )
        data = response.get_json()
        assert data["running"] is False


class TestApiTestNotificationUrl:
    """Tests for /api/notifications/test-url endpoint."""

    def test_requires_authentication(self, client):
        response = client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
            json={"service_url": "http://example.com"},
        )
        assert response.status_code in [401, 302]

    @patch("local_deep_research.notifications.service.NotificationService")
    def test_successful_test(self, mock_ns_cls, authenticated_client):
        mock_ns = MagicMock()
        mock_ns.test_service.return_value = {
            "success": True,
            "message": "Notification sent",
            "error": "",
        }
        mock_ns_cls.return_value = mock_ns

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
            json={"service_url": "tgram://token/chat_id"},
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["success"] is True

    @patch("local_deep_research.notifications.service.NotificationService")
    def test_failed_test(self, mock_ns_cls, authenticated_client):
        mock_ns = MagicMock()
        mock_ns.test_service.return_value = {
            "success": False,
            "message": "",
            "error": "Invalid URL",
        }
        mock_ns_cls.return_value = mock_ns

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
            json={"service_url": "invalid://url"},
        )
        data = response.get_json()
        assert data["success"] is False

    def test_missing_service_url(self, authenticated_client):
        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
            json={},
        )
        assert response.status_code == 400
        data = response.get_json()
        assert data["success"] is False

    def test_no_json_body(self, authenticated_client):
        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
        )
        # No JSON body causes get_json() to return None, hitting the error handler
        assert response.status_code in [400, 500]

    @patch("local_deep_research.notifications.service.NotificationService")
    def test_does_not_leak_internal_details(
        self, mock_ns_cls, authenticated_client
    ):
        """Response should only contain expected safe fields."""
        mock_ns = MagicMock()
        mock_ns.test_service.return_value = {
            "success": True,
            "message": "OK",
            "error": "",
            "internal_debug": "SECRET_TOKEN_123",
        }
        mock_ns_cls.return_value = mock_ns

        response = authenticated_client.post(
            f"{SETTINGS_PREFIX}/api/notifications/test-url",
            json={"service_url": "tgram://token/chat_id"},
        )
        data = response.get_json()
        assert "internal_debug" not in data
        assert "SECRET_TOKEN_123" not in str(data)
