"""
Unit tests for health check API endpoints

Tests the /api/health and /api/health/db endpoints that are used by
load balancers and monitoring systems to verify service availability.
"""
import pytest
from httpx import AsyncClient, ASGITransport
from unittest.mock import patch, AsyncMock
import pendulum
from app.main import app


@pytest.fixture
async def client():
    """Async HTTP test client for API requests"""
    async with AsyncClient(
        transport=ASGITransport(app=app),
        base_url="http://test"
    ) as ac:
        yield ac


class TestBasicHealthEndpoint:
    """Tests for GET /api/health endpoint"""

    @pytest.mark.asyncio
    async def test_health_returns_200(self, client):
        """Test basic health endpoint returns 200 status"""
        response = await client.get("/api/health")
        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_health_response_structure(self, client):
        """Test health response has all required fields"""
        response = await client.get("/api/health")
        data = response.json()

        # Verify all required fields present
        assert "status" in data
        assert "timestamp" in data
        assert "environment" in data
        assert "version" in data

    @pytest.mark.asyncio
    async def test_health_status_value(self, client):
        """Test health status is 'healthy'"""
        response = await client.get("/api/health")
        data = response.json()

        assert data["status"] == "healthy"

    @pytest.mark.asyncio
    async def test_health_timestamp_format(self, client):
        """Test timestamp is valid ISO8601 format"""
        response = await client.get("/api/health")
        data = response.json()

        # Should not raise exception when parsing
        timestamp = pendulum.parse(data["timestamp"])
        assert timestamp is not None

        # Should be recent (within last minute)
        now = pendulum.now('UTC')
        age = now - timestamp
        assert age.total_seconds() < 60  # Less than 1 minute old

    @pytest.mark.asyncio
    async def test_health_environment_field(self, client):
        """Test environment field is populated"""
        response = await client.get("/api/health")
        data = response.json()

        assert "environment" in data
        assert isinstance(data["environment"], str)
        # Should be one of the valid environments
        assert data["environment"] in ["development", "staging", "production"]

    @pytest.mark.asyncio
    async def test_health_version_field(self, client):
        """Test version field is present and valid"""
        response = await client.get("/api/health")
        data = response.json()

        assert "version" in data
        assert data["version"] == "1.0.0"


class TestDatabaseHealthEndpoint:
    """Tests for GET /api/health/db endpoint

    Note: Database error scenarios (connection failures, timeouts) are tested
    in integration tests where we can control the database state. Mocking
    SQLAlchemy's AsyncEngine is problematic due to read-only attributes.
    """

    @pytest.mark.asyncio
    async def test_db_health_returns_200(self, client):
        """Test database health endpoint returns 200 status"""
        response = await client.get("/api/health/db")
        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_db_health_response_structure(self, client):
        """Test database health response has all required fields"""
        response = await client.get("/api/health/db")
        data = response.json()

        assert "status" in data
        assert "database" in data
        assert "timestamp" in data
        # Note: status can be "healthy" or "unhealthy" depending on DB state

    @pytest.mark.asyncio
    async def test_db_health_timestamp_format(self, client):
        """Test DB health timestamp is valid ISO8601 format"""
        response = await client.get("/api/health/db")
        data = response.json()

        # Should not raise exception when parsing
        timestamp = pendulum.parse(data["timestamp"])
        assert timestamp is not None

        # Should be recent (within last minute)
        now = pendulum.now('UTC')
        age = now - timestamp
        assert age.total_seconds() < 60

    @pytest.mark.asyncio
    async def test_db_health_status_values(self, client):
        """Test database health status is either healthy or unhealthy"""
        response = await client.get("/api/health/db")
        data = response.json()

        # Status should be one of the expected values
        assert data["status"] in ["healthy", "unhealthy"]

        # Database field should be one of the expected values
        assert data["database"] in ["connected", "disconnected"]

        # If unhealthy, should have error field
        if data["status"] == "unhealthy":
            assert "error" in data


class TestHealthEndpointIntegration:
    """Integration tests for health endpoints"""

    @pytest.mark.asyncio
    async def test_both_endpoints_accessible(self, client):
        """Test both health endpoints are accessible"""
        basic_response = await client.get("/api/health")
        db_response = await client.get("/api/health/db")

        assert basic_response.status_code == 200
        assert db_response.status_code == 200

    @pytest.mark.asyncio
    async def test_health_endpoint_performance(self, client):
        """Test health endpoint responds quickly"""
        import time

        start = time.time()
        response = await client.get("/api/health")
        duration = time.time() - start

        assert response.status_code == 200
        # Should respond in less than 100ms
        assert duration < 0.1

    @pytest.mark.asyncio
    async def test_db_health_endpoint_performance(self, client):
        """Test DB health endpoint responds reasonably quickly"""
        import time

        start = time.time()
        response = await client.get("/api/health/db")
        duration = time.time() - start

        assert response.status_code == 200
        # Should respond in less than 1 second
        assert duration < 1.0

    @pytest.mark.asyncio
    async def test_health_endpoints_consistency(self, client):
        """Test multiple calls return consistent data"""
        responses = []
        for _ in range(3):
            response = await client.get("/api/health")
            responses.append(response.json())

        # All should have same status and version
        for data in responses:
            assert data["status"] == "healthy"
            assert data["version"] == "1.0.0"
            assert data["environment"] == responses[0]["environment"]
