"""Tests for Prometheus metrics collection."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock

import pytest
from starlette.requests import Request
from starlette.responses import PlainTextResponse

from app.middleware.metrics import MetricsMiddleware, _normalize_path
from app.utils.metrics import get_metrics


class TestMetricsUtils:
    """Test metrics utility functions."""

    def test_normalize_path_with_uuid(self) -> None:
        """Test path normalization with UUID."""
        path = "/api/resource/550e8400-e29b-41d4-a716-446655440000"
        normalized = _normalize_path(path)
        assert normalized == "/api/{id}"

    def test_normalize_path_with_numeric_id(self) -> None:
        """Test path normalization with numeric ID."""
        path = "/api/resource/123"
        normalized = _normalize_path(path)
        assert normalized == "/api/{id}"

    def test_normalize_path_without_id(self) -> None:
        """Test path without ID remains unchanged."""
        path = "/api/resource"
        normalized = _normalize_path(path)
        assert normalized == "/api/resource"

    def test_get_metrics_returns_bytes(self) -> None:
        """Test that get_metrics returns bytes."""
        metrics = get_metrics()
        assert isinstance(metrics, bytes)


@pytest.mark.asyncio
class TestMetricsMiddleware:
    """Test metrics collection middleware."""

    async def test_middleware_tracks_request_metrics(self) -> None:
        """Test middleware tracks request metrics."""
        middleware = MetricsMiddleware(app=MagicMock())

        request = MagicMock(spec=Request)
        request.method = "GET"
        request.url.path = "/api/test"

        response = PlainTextResponse("OK")
        response.status_code = 200

        call_next = AsyncMock(return_value=response)

        result = await middleware.dispatch(request, call_next)

        assert result == response
        assert call_next.called

    async def test_middleware_skips_metrics_endpoint(self) -> None:
        """Test middleware skips /metrics endpoint."""
        middleware = MetricsMiddleware(app=MagicMock())

        request = MagicMock(spec=Request)
        request.method = "GET"
        request.url.path = "/metrics"

        response = PlainTextResponse("metrics")
        response.status_code = 200

        call_next = AsyncMock(return_value=response)

        result = await middleware.dispatch(request, call_next)

        assert result == response

    async def test_middleware_tracks_error_responses(self) -> None:
        """Test middleware tracks error response status codes."""
        middleware = MetricsMiddleware(app=MagicMock())

        request = MagicMock(spec=Request)
        request.method = "GET"
        request.url.path = "/api/test"

        response = PlainTextResponse("Not Found")
        response.status_code = 404

        call_next = AsyncMock(return_value=response)

        result = await middleware.dispatch(request, call_next)

        assert result == response
        assert result.status_code == 404

    async def test_middleware_handles_exceptions(self) -> None:
        """Test middleware handles exceptions during request processing."""
        middleware = MetricsMiddleware(app=MagicMock())

        request = MagicMock(spec=Request)
        request.method = "GET"
        request.url.path = "/api/test"

        call_next = AsyncMock(side_effect=RuntimeError("Test error"))

        with pytest.raises(RuntimeError):
            await middleware.dispatch(request, call_next)


@pytest.mark.asyncio
class TestMetricsEndpoint:
    """Test the /metrics endpoint."""

    async def test_metrics_endpoint_returns_prometheus_format(self) -> None:
        """Test metrics endpoint returns Prometheus format."""
        from app.routers.metrics import get_application_metrics

        response = await get_application_metrics()

        assert response.status_code == 200
        assert response.media_type.startswith("text/plain")
        assert b"bangui_http_requests_total" in response.body
