"""Unit tests for health check endpoints."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.server.api.health import (
    DatabaseHealth,
    HealthStatus,
    SystemMetrics,
    basic_health_check,
    check_database_health,
    check_filesystem_health,
    get_system_metrics,
    ready_check,
)


@pytest.mark.asyncio
async def test_basic_health_check_no_startup_checks():
    """Test basic health check endpoint with no startup checks."""
    mock_request = MagicMock()
    mock_request.app.state.startup_checks = {}
    
    with patch("src.config.settings.settings") as mock_settings, \
         patch("src.server.utils.dependencies._series_app", None):
        mock_settings.anime_directory = ""
        result = await basic_health_check(mock_request)

        assert isinstance(result, HealthStatus)
        assert result.status == "healthy"
        assert result.version == "1.0.1"
        assert result.service == "aniworld-api"
        assert result.timestamp is not None
        assert result.series_app_initialized is False
        assert result.anime_directory_configured is False


@pytest.mark.asyncio
async def test_basic_health_check_with_error_check():
    """Test basic health check reflects error status from startup checks."""
    mock_request = MagicMock()
    mock_request.app.state.startup_checks = {
        "anime_directory": {"status": "error", "message": "not configured", "path": None},
        "ffmpeg": {"status": "ok", "message": "Found at /usr/bin/ffmpeg"},
        "dns_aniworld": {"status": "ok", "message": "Resolved successfully"},
        "dns_tmdb": {"status": "ok", "message": "Resolved successfully"},
    }
    
    with patch("src.config.settings.settings") as mock_settings, \
         patch("src.server.utils.dependencies._series_app", None):
        mock_settings.anime_directory = ""
        result = await basic_health_check(mock_request)

        assert isinstance(result, HealthStatus)
        assert result.status == "unhealthy"
        assert result.checks is not None
        assert result.checks["anime_directory"]["status"] == "error"


@pytest.mark.asyncio
async def test_basic_health_check_with_warning_only():
    """Test basic health check shows degraded when only warnings present."""
    mock_request = MagicMock()
    mock_request.app.state.startup_checks = {
        "anime_directory": {"status": "ok", "message": "Found", "path": "/anime"},
        "ffmpeg": {"status": "warning", "message": "not found in PATH"},
        "dns_aniworld": {"status": "ok", "message": "Resolved successfully"},
        "dns_tmdb": {"status": "ok", "message": "Resolved successfully"},
    }
    
    with patch("src.config.settings.settings") as mock_settings, \
         patch("src.server.utils.dependencies._series_app", None):
        mock_settings.anime_directory = "/anime"
        result = await basic_health_check(mock_request)

        assert isinstance(result, HealthStatus)
        assert result.status == "degraded"


@pytest.mark.asyncio
async def test_ready_check_all_healthy():
    """Test ready check returns ready when all checks pass."""
    mock_request = MagicMock()
    mock_request.app.state.startup_checks = {
        "anime_directory": {"status": "ok", "message": "Found", "path": "/anime"},
        "ffmpeg": {"status": "ok", "message": "Found at /usr/bin/ffmpeg"},
        "dns_aniworld": {"status": "ok", "message": "Resolved successfully"},
        "dns_tmdb": {"status": "ok", "message": "Resolved successfully"},
    }
    
    result = await ready_check(mock_request)
    
    assert result["ready"] is True
    assert result["status"] == "ready"
    assert "critical_failures" not in result


@pytest.mark.asyncio
async def test_ready_check_with_critical_failure():
    """Test ready check returns not_ready when anime_directory not configured."""
    mock_request = MagicMock()
    mock_request.app.state.startup_checks = {
        "anime_directory": {"status": "error", "message": "not configured", "path": None},
        "ffmpeg": {"status": "warning", "message": "not found in PATH"},
        "dns_aniworld": {"status": "ok", "message": "Resolved successfully"},
        "dns_tmdb": {"status": "ok", "message": "Resolved successfully"},
    }
    
    result = await ready_check(mock_request)
    
    assert result["ready"] is False
    assert result["status"] == "not_ready"
    assert len(result["critical_failures"]) == 1
    assert "anime_directory" in result["critical_failures"][0]


@pytest.mark.asyncio
async def test_database_health_check_success():
    """Test database health check with successful connection."""
    # Mock database session
    mock_db = AsyncMock()
    mock_db.execute = AsyncMock()

    result = await check_database_health(mock_db)

    assert isinstance(result, DatabaseHealth)
    assert result.status == "healthy"
    assert result.connection_time_ms >= 0
    assert "successful" in result.message.lower()


@pytest.mark.asyncio
async def test_database_health_check_failure():
    """Test database health check with failed connection."""
    # Mock database session that raises error
    mock_db = AsyncMock()
    mock_db.execute = AsyncMock(side_effect=Exception("Connection failed"))

    result = await check_database_health(mock_db)

    assert isinstance(result, DatabaseHealth)
    assert result.status == "unhealthy"
    assert "failed" in result.message.lower()


@pytest.mark.asyncio
async def test_filesystem_health_check_success():
    """Test filesystem health check with accessible directories."""
    with patch("os.path.exists", return_value=True), patch(
        "os.access", return_value=True
    ):
        result = await check_filesystem_health()

        assert result["status"] in ["healthy", "degraded"]
        assert "data_dir_writable" in result
        assert "logs_dir_writable" in result


@pytest.mark.asyncio
async def test_filesystem_health_check_failure():
    """Test filesystem health check with inaccessible directories."""
    with patch("os.path.exists", return_value=False), patch(
        "os.access", return_value=False
    ):
        result = await check_filesystem_health()

        assert "status" in result
        assert "message" in result


def test_get_system_metrics():
    """Test system metrics collection."""
    result = get_system_metrics()

    assert isinstance(result, SystemMetrics)
    assert result.cpu_percent >= 0
    assert result.memory_percent >= 0
    assert result.memory_available_mb > 0
    assert result.disk_percent >= 0
    assert result.disk_free_mb > 0
    assert result.uptime_seconds > 0


def test_system_metrics_values_reasonable():
    """Test that system metrics are within reasonable ranges."""
    result = get_system_metrics()

    # CPU should be 0-100%
    assert 0 <= result.cpu_percent <= 100

    # Memory should be 0-100%
    assert 0 <= result.memory_percent <= 100

    # Disk should be 0-100%
    assert 0 <= result.disk_percent <= 100

    # Memory available should be positive
    assert result.memory_available_mb > 0

    # Disk free should be positive
    assert result.disk_free_mb > 0

    # Uptime should be positive
    assert result.uptime_seconds > 0
