# tests/api/routes/test_health.py
from datetime import datetime
from unittest.mock import patch

import pytest
from fastapi import status
from fastapi.testclient import TestClient

from app.main import app


@pytest.fixture
def client():
    """Create a FastAPI test client for the main app with mocked database."""
    # Mock check_database_health to avoid connecting to the actual database
    with patch("app.main.check_database_health") as mock_health_check:
        # By default, return True (healthy)
        mock_health_check.return_value = True
        yield TestClient(app)


class TestHealthEndpoint:
    """Tests for the /health endpoint"""

    def test_health_check_healthy(self, client):
        """Test that health check returns healthy when database is accessible"""
        response = client.get("/health")

        assert response.status_code == status.HTTP_200_OK
        data = response.json()

        # Check required fields
        assert "status" in data
        assert data["status"] == "healthy"
        assert "timestamp" in data
        assert "version" in data
        assert "environment" in data
        assert "checks" in data

        # Verify timestamp format (ISO 8601)
        assert data["timestamp"].endswith("Z")
        # Verify it's a valid datetime
        datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00"))

        # Check database health
        assert "database" in data["checks"]
        assert data["checks"]["database"]["status"] == "healthy"
        assert "message" in data["checks"]["database"]

    def test_health_check_response_structure(self, client):
        """Test that health check response has correct structure"""
        response = client.get("/health")
        data = response.json()

        # Verify top-level structure
        assert isinstance(data["status"], str)
        assert isinstance(data["timestamp"], str)
        assert isinstance(data["version"], str)
        assert isinstance(data["environment"], str)
        assert isinstance(data["checks"], dict)

        # Verify database check structure
        db_check = data["checks"]["database"]
        assert isinstance(db_check["status"], str)
        assert isinstance(db_check["message"], str)

    def test_health_check_version_matches_settings(self, client):
        """Test that health check returns correct version from settings"""
        from app.core.config import settings

        response = client.get("/health")
        data = response.json()

        assert data["version"] == settings.VERSION

    def test_health_check_environment_matches_settings(self, client):
        """Test that health check returns correct environment from settings"""
        from app.core.config import settings

        response = client.get("/health")
        data = response.json()

        assert data["environment"] == settings.ENVIRONMENT

    def test_health_check_database_connection_failure(self):
        """Test that health check returns unhealthy when database is not accessible"""
        # Mock check_database_health to return False (unhealthy)
        with patch("app.main.check_database_health") as mock_health_check:
            mock_health_check.return_value = False

            test_client = TestClient(app)
            response = test_client.get("/health")

            assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
            data = response.json()

            # Check overall status
            assert data["status"] == "unhealthy"

            # Check database status
            assert "database" in data["checks"]
            assert data["checks"]["database"]["status"] == "unhealthy"
            assert "failed" in data["checks"]["database"]["message"].lower()

    def test_health_check_timestamp_recent(self, client):
        """Test that health check timestamp is recent (within last minute)"""
        before = datetime.utcnow()
        response = client.get("/health")
        after = datetime.utcnow()

        data = response.json()
        timestamp = datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00"))

        # Timestamp should be between before and after
        assert before <= timestamp.replace(tzinfo=None) <= after

    def test_health_check_no_authentication_required(self, client):
        """Test that health check does not require authentication"""
        # Make request without any authentication headers
        response = client.get("/health")

        # Should succeed without authentication
        assert response.status_code in [
            status.HTTP_200_OK,
            status.HTTP_503_SERVICE_UNAVAILABLE,
        ]

    def test_health_check_idempotent(self, client):
        """Test that multiple health checks return consistent results"""
        response1 = client.get("/health")
        response2 = client.get("/health")

        # Both should have same status code (either both healthy or both unhealthy)
        assert response1.status_code == response2.status_code

        data1 = response1.json()
        data2 = response2.json()

        # Same overall health status
        assert data1["status"] == data2["status"]

        # Same version and environment
        assert data1["version"] == data2["version"]
        assert data1["environment"] == data2["environment"]

        # Same database check status
        assert (
            data1["checks"]["database"]["status"]
            == data2["checks"]["database"]["status"]
        )

    def test_health_check_content_type(self, client):
        """Test that health check returns JSON content type"""
        response = client.get("/health")

        assert "application/json" in response.headers["content-type"]


class TestHealthEndpointEdgeCases:
    """Edge case tests for the /health endpoint"""

    def test_health_check_with_query_parameters(self, client):
        """Test that health check ignores query parameters"""
        response = client.get("/health?foo=bar&baz=qux")

        # Should still work with query params
        assert response.status_code == status.HTTP_200_OK

    def test_health_check_method_not_allowed(self, client):
        """Test that POST/PUT/DELETE are not allowed on health endpoint"""
        # POST should not be allowed
        response = client.post("/health")
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

        # PUT should not be allowed
        response = client.put("/health")
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

        # DELETE should not be allowed
        response = client.delete("/health")
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

    def test_health_check_with_accept_header(self, client):
        """Test that health check works with different Accept headers"""
        response = client.get("/health", headers={"Accept": "application/json"})
        assert response.status_code == status.HTTP_200_OK

        response = client.get("/health", headers={"Accept": "*/*"})
        assert response.status_code == status.HTTP_200_OK
