"""Tests for app.core.monitoring — performance monitoring, Sentry, alert window"""

import time
import pytest
from unittest.mock import AsyncMock, MagicMock, patch


def test_init_sentry_no_dsn():
    from app.core.monitoring import init_sentry, SENTRY_DSN
    init_sentry(None)
    assert SENTRY_DSN is None


def test_init_sentry_missing_sdk():
    """init_sentry should handle missing sentry-sdk gracefully (no ImportError)"""
    from app.core.monitoring import init_sentry
    # Remove sentry_sdk from sys.modules if present, then restore
    import sys
    had_it = "sentry_sdk" in sys.modules
    old = sys.modules.pop("sentry_sdk", None)
    try:
        init_sentry("https://key@sentry.io/123")
    finally:
        if had_it:
            sys.modules["sentry_sdk"] = old


def test_init_sentry_success():
    from app.core.monitoring import init_sentry
    import sys
    mock_sentry = MagicMock()
    sys.modules["sentry_sdk"] = mock_sentry
    try:
        init_sentry("https://key@sentry.io/123")
        mock_sentry.init.assert_called_once_with(
            dsn="https://key@sentry.io/123",
            traces_sample_rate=0.1,
            profiles_sample_rate=0.05,
        )
    finally:
        del sys.modules["sentry_sdk"]


@pytest.mark.asyncio
async def test_performance_middleware_fast():
    from app.core.monitoring import PerformanceMiddleware
    from fastapi import Request
    from fastapi.responses import JSONResponse

    mock_request = MagicMock(spec=Request)
    mock_request.method = "GET"
    mock_request.url.path = "/health"
    mock_call_next = AsyncMock(return_value=JSONResponse(content={}, status_code=200))

    mw = PerformanceMiddleware(object())
    response = await mw.dispatch(mock_request, mock_call_next)

    assert response.status_code == 200
    assert "X-Response-Time-Ms" in response.headers


@pytest.mark.asyncio
async def test_performance_middleware_slow():
    from app.core.monitoring import PerformanceMiddleware
    from fastapi import Request
    from fastapi.responses import JSONResponse

    mock_request = MagicMock(spec=Request)
    mock_request.method = "POST"
    mock_request.url.path = "/slow"

    mw = PerformanceMiddleware(object())
    mw.SLOW_THRESHOLD_MS = 1  # low enough that any real work triggers

    async def slow_call_next(request):
        import asyncio
        await asyncio.sleep(0.02)
        return JSONResponse(content={}, status_code=200)

    response = await mw.dispatch(mock_request, slow_call_next)
    assert response.status_code == 200
    assert "X-Response-Time-Ms" in response.headers


@pytest.mark.asyncio
async def test_performance_middleware_5xx():
    from app.core.monitoring import PerformanceMiddleware, _alert_window
    from fastapi import Request
    from fastapi.responses import JSONResponse

    _alert_window.clear()
    mock_request = MagicMock(spec=Request)
    mock_request.method = "GET"
    mock_request.url.path = "/error"
    mock_call_next = AsyncMock(return_value=JSONResponse(content={}, status_code=500))

    mw = PerformanceMiddleware(object())
    response = await mw.dispatch(mock_request, mock_call_next)
    assert response.status_code == 500

    # alert window should have an entry
    assert len(_alert_window) > 0


@pytest.mark.asyncio
async def test_request_logging_middleware():
    from app.core.monitoring import RequestLoggingMiddleware
    from fastapi import Request
    from fastapi.responses import JSONResponse

    mock_request = MagicMock(spec=Request)
    mock_request.method = "GET"
    mock_request.url.path = "/health"
    mock_call_next = AsyncMock(return_value=JSONResponse(content={}, status_code=200))

    mw = RequestLoggingMiddleware(object())
    response = await mw.dispatch(mock_request, mock_call_next)
    assert response.status_code == 200


def test_check_alert_below_threshold():
    from app.core.monitoring import _check_alert, _alert_window

    _alert_window.clear()
    _check_alert("/test")
    bucket_key = f"5xx:{time.time() // 60}"
    assert bucket_key in _alert_window
    assert _alert_window[bucket_key][0] == 1


def test_check_alert_old_cleanup():
    from app.core.monitoring import _check_alert, _alert_window

    _alert_window.clear()
    _alert_window["5xx:0"] = (100, 0)  # very old bucket
    _check_alert("/test")
    # old bucket should be cleaned up
    assert "5xx:0" not in _alert_window


def test_get_metrics():
    from app.core.monitoring import get_metrics, _alert_window

    _alert_window.clear()
    _alert_window["5xx:1000"] = (5, time.time())
    metrics = get_metrics()
    assert "recent_5xx" in metrics
    assert "alert_active" in metrics
    assert metrics["recent_5xx"]["5xx:1000"] == 5
    assert metrics["alert_active"] is False


def test_get_metrics_alert_active():
    from app.core.monitoring import get_metrics, _alert_window, ALERT_THRESHOLD_5XX_PER_MIN

    _alert_window.clear()
    _alert_window["5xx:2000"] = (ALERT_THRESHOLD_5XX_PER_MIN, time.time())
    metrics = get_metrics()
    assert metrics["alert_active"] is True
