"""Unit tests for health endpoint."""

from contextlib import asynccontextmanager
from typing import Any
from unittest.mock import AsyncMock, MagicMock

import pytest
from fastapi import status
from httpx import AsyncClient

from vmk_data_collector.api.v1 import router_health


def _make_mock_engine(healthy: bool = True) -> Any:
    mock_conn = AsyncMock()
    mock_conn.execute = AsyncMock()

    @asynccontextmanager
    async def connect_cm():
        if not healthy:
            raise RuntimeError("db down")
        yield mock_conn

    engine = MagicMock()
    engine.connect = connect_cm
    return engine


def _make_fake_httpx_client(healthy: bool = True) -> type:
    class FakeClient:
        def __init__(self, *a: Any, **k: Any) -> None:
            pass

        async def __aenter__(self):
            return self

        async def __aexit__(self, *a: Any) -> None:
            pass

        async def get(self, *a: Any, **k: Any) -> Any:
            if not healthy:
                raise RuntimeError("ollama down")
            resp = MagicMock()
            resp.raise_for_status = lambda: None
            return resp

    return FakeClient


class TestHealthCheck:
    @pytest.mark.asyncio
    async def test_all_healthy_returns_200(
        self,
        async_client: AsyncClient,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(router_health, "engine", _make_mock_engine(healthy=True))
        monkeypatch.setattr(
            router_health.httpx,
            "AsyncClient",
            _make_fake_httpx_client(healthy=True),
        )
        monkeypatch.setattr(
            router_health.shutil,
            "disk_usage",
            lambda _p: MagicMock(free=500 * 1024 * 1024),
        )

        response = await async_client.get("/api/v1/health")
        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["status"] == "healthy"
        for check in data["checks"].values():
            assert check["status"] == "pass"

    @pytest.mark.asyncio
    async def test_db_fails_returns_503(
        self,
        async_client: AsyncClient,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(router_health, "engine", _make_mock_engine(healthy=False))
        monkeypatch.setattr(
            router_health.httpx,
            "AsyncClient",
            _make_fake_httpx_client(healthy=True),
        )
        monkeypatch.setattr(
            router_health.shutil,
            "disk_usage",
            lambda _p: MagicMock(free=500 * 1024 * 1024),
        )

        response = await async_client.get("/api/v1/health")
        assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
        data = response.json()
        assert data["status"] == "degraded"
        assert data["checks"]["database"]["status"] == "fail"
        assert data["checks"]["ollama"]["status"] == "pass"

    @pytest.mark.asyncio
    async def test_ollama_fails_returns_503(
        self,
        async_client: AsyncClient,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(router_health, "engine", _make_mock_engine(healthy=True))
        monkeypatch.setattr(
            router_health.httpx,
            "AsyncClient",
            _make_fake_httpx_client(healthy=False),
        )
        monkeypatch.setattr(
            router_health.shutil,
            "disk_usage",
            lambda _p: MagicMock(free=500 * 1024 * 1024),
        )

        response = await async_client.get("/api/v1/health")
        assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
        data = response.json()
        assert data["checks"]["ollama"]["status"] == "fail"

    @pytest.mark.asyncio
    async def test_low_disk_returns_503(
        self,
        async_client: AsyncClient,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setattr(router_health, "engine", _make_mock_engine(healthy=True))
        monkeypatch.setattr(
            router_health.httpx,
            "AsyncClient",
            _make_fake_httpx_client(healthy=True),
        )
        monkeypatch.setattr(
            router_health.shutil,
            "disk_usage",
            lambda _p: MagicMock(free=10),  # way below 100 MB
        )

        response = await async_client.get("/api/v1/health")
        assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
        data = response.json()
        assert data["checks"]["disk"]["status"] == "fail"
