# allow: no-sut-import — black-box HTTP test; drives real routes through the Flask test client
"""Tests for api_current_rate_limits and api_cost_calculation in metrics_routes.py."""

import time
from contextlib import contextmanager
from unittest.mock import patch, MagicMock

METRICS_PREFIX = "/metrics"


def _make_estimate(
    engine_type, base_wait, min_wait, max_wait, ts, attempts, rate
):
    """Create a mock RateLimitEstimate object."""
    est = MagicMock()
    est.engine_type = engine_type
    est.base_wait_seconds = base_wait
    est.min_wait_seconds = min_wait
    est.max_wait_seconds = max_wait
    est.last_updated = ts
    est.total_attempts = attempts
    est.success_rate = rate
    return est


class TestApiCurrentRateLimits:
    """Tests for /api/rate-limiting/current endpoint."""

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

    @patch("local_deep_research.web.routes.metrics_routes.get_user_db_session")
    def test_returns_current_limits(
        self, mock_get_session, authenticated_client
    ):
        now = time.time()
        mock_session = MagicMock()
        mock_session.query.return_value.order_by.return_value.all.return_value = [
            _make_estimate("google", 2.0, 1.0, 5.0, now, 100, 0.95),
            _make_estimate("bing", 1.5, 0.5, 3.0, now, 50, 0.6),
        ]

        @contextmanager
        def _ctx(username):
            yield mock_session

        mock_get_session.side_effect = _ctx

        response = authenticated_client.get(
            f"{METRICS_PREFIX}/api/rate-limiting/current"
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["status"] == "success"
        assert len(data["current_limits"]) == 2

        google = data["current_limits"][0]
        assert google["engine_type"] == "google"
        assert google["base_wait_seconds"] == 2.0
        assert google["success_rate"] == 95.0
        assert google["status"] == "healthy"

        bing = data["current_limits"][1]
        assert bing["status"] == "degraded"

    @patch("local_deep_research.web.routes.metrics_routes.get_user_db_session")
    def test_poor_status_threshold(
        self, mock_get_session, authenticated_client
    ):
        now = time.time()
        mock_session = MagicMock()
        mock_session.query.return_value.order_by.return_value.all.return_value = [
            _make_estimate("failing_engine", 10.0, 5.0, 30.0, now, 200, 0.3),
        ]

        @contextmanager
        def _ctx(username):
            yield mock_session

        mock_get_session.side_effect = _ctx

        response = authenticated_client.get(
            f"{METRICS_PREFIX}/api/rate-limiting/current"
        )
        data = response.get_json()
        assert data["current_limits"][0]["status"] == "poor"

    @patch("local_deep_research.web.routes.metrics_routes.get_user_db_session")
    def test_empty_stats(self, mock_get_session, authenticated_client):
        mock_session = MagicMock()
        mock_session.query.return_value.order_by.return_value.all.return_value = []

        @contextmanager
        def _ctx(username):
            yield mock_session

        mock_get_session.side_effect = _ctx

        response = authenticated_client.get(
            f"{METRICS_PREFIX}/api/rate-limiting/current"
        )
        data = response.get_json()
        assert data["status"] == "success"
        assert data["current_limits"] == []

    @patch("local_deep_research.web.routes.metrics_routes.get_user_db_session")
    def test_error_returns_500(self, mock_get_session, authenticated_client):
        mock_get_session.side_effect = RuntimeError("db error")

        response = authenticated_client.get(
            f"{METRICS_PREFIX}/api/rate-limiting/current"
        )
        assert response.status_code == 500
        data = response.get_json()
        assert data["status"] == "error"


class TestApiCostCalculation:
    """Tests for /api/cost-calculation endpoint."""

    def test_requires_authentication(self, client):
        response = client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
            json={"model_name": "gpt-4", "prompt_tokens": 100},
        )
        assert response.status_code in [401, 302]

    @patch("local_deep_research.metrics.pricing.cost_calculator.CostCalculator")
    def test_successful_calculation(self, mock_cc_cls, authenticated_client):
        mock_calc = MagicMock()
        mock_calc.calculate_cost_sync.return_value = {
            "prompt_cost": 0.003,
            "completion_cost": 0.006,
            "total_cost": 0.009,
        }
        mock_cc_cls.return_value = mock_calc

        response = authenticated_client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
            json={
                "model_name": "gpt-4",
                "provider": "openai",
                "prompt_tokens": 100,
                "completion_tokens": 200,
            },
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["status"] == "success"
        assert data["model_name"] == "gpt-4"
        assert data["total_tokens"] == 300
        assert data["total_cost"] == 0.009

    def test_missing_model_name(self, authenticated_client):
        response = authenticated_client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
            json={"prompt_tokens": 100},
        )
        assert response.status_code == 400
        data = response.get_json()
        assert "model_name" in data["error"].lower()

    def test_no_json_body(self, authenticated_client):
        response = authenticated_client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
        )
        assert response.status_code == 400

    @patch("local_deep_research.metrics.pricing.cost_calculator.CostCalculator")
    def test_defaults_to_zero_tokens(self, mock_cc_cls, authenticated_client):
        mock_calc = MagicMock()
        mock_calc.calculate_cost_sync.return_value = {"total_cost": 0.0}
        mock_cc_cls.return_value = mock_calc

        response = authenticated_client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
            json={"model_name": "gpt-4"},
        )
        assert response.status_code == 200
        data = response.get_json()
        assert data["prompt_tokens"] == 0
        assert data["completion_tokens"] == 0
        assert data["total_tokens"] == 0

    @patch("local_deep_research.metrics.pricing.cost_calculator.CostCalculator")
    def test_error_returns_500(self, mock_cc_cls, authenticated_client):
        mock_cc_cls.side_effect = RuntimeError("pricing error")

        response = authenticated_client.post(
            f"{METRICS_PREFIX}/api/cost-calculation",
            json={"model_name": "gpt-4"},
        )
        assert response.status_code == 500
