"""Test unified error response schema.

This test suite verifies that all error responses use the unified ErrorResponse
format with code, detail, metadata, and correlation_id fields.

This ensures:
1. Frontend can handle all errors with a single schema.
2. Error codes are machine-readable.
3. Error details include structured metadata.
"""

from __future__ import annotations

from typing import Any

import pytest
from httpx import ASGITransport, AsyncClient
from pydantic import BaseModel

from app.config import Settings
from app.main import create_app


class RequestModel(BaseModel):
    """Test request model with validation."""

    name: str
    age: int


class TestErrorResponseSchema:
    """Verify all error responses use the unified ErrorResponse schema."""

    @pytest.mark.asyncio
    async def test_validation_error_returns_unified_schema(
        self, test_settings: Settings
    ) -> None:
        """RequestValidationError returns unified ErrorResponse format."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            # Send invalid data that violates validation
            response = await client.post(
                "/test-validation", json={"name": "John", "age": "not_a_number"}
            )

            assert response.status_code == 400
            data = response.json()

            # Verify unified schema
            assert "code" in data, "Missing 'code' field"
            assert "detail" in data, "Missing 'detail' field"
            assert "metadata" in data, "Missing 'metadata' field"
            assert "correlation_id" in data, "Missing 'correlation_id' field"

            # Verify values
            assert data["code"] == "invalid_input"
            assert "validation" in data["detail"].lower()
            assert isinstance(data["metadata"], dict)

    @pytest.mark.asyncio
    async def test_validation_error_includes_field_details(
        self, test_settings: Settings
    ) -> None:
        """Validation error metadata includes field error count and first field."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            response = await client.post(
                "/test-validation", json={"name": "John", "age": "invalid"}
            )

            assert response.status_code == 400
            data = response.json()

            # Verify metadata contains field error information
            assert "field_errors" in data["metadata"]
            assert "first_field" in data["metadata"]
            assert data["metadata"]["field_errors"] > 0
            assert "age" in data["metadata"]["first_field"]

    @pytest.mark.asyncio
    async def test_validation_error_has_correlation_id(
        self, test_settings: Settings
    ) -> None:
        """Validation errors include correlation_id for tracing."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            response = await client.post(
                "/test-validation", json={"name": 123, "age": "not_a_number"}
            )

            assert response.status_code == 400
            data = response.json()

            # correlation_id can be None but field should exist
            assert "correlation_id" in data

    @pytest.mark.asyncio
    async def test_missing_required_field_validation(
        self, test_settings: Settings
    ) -> None:
        """Missing required fields return validation error."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            # Send incomplete data
            response = await client.post("/test-validation", json={"name": "John"})

            assert response.status_code == 400
            data = response.json()

            assert data["code"] == "invalid_input"
            assert "validation" in data["detail"].lower()
            assert "field_errors" in data["metadata"]
            assert data["metadata"]["field_errors"] > 0

    @pytest.mark.asyncio
    async def test_validation_error_response_structure(self, test_settings: Settings) -> None:
        """All validation errors return consistent ErrorResponse structure."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            # Test with wrong type
            response = await client.post(
                "/test-validation",
                json={"name": "John", "age": []},
            )

            assert response.status_code == 400
            data = response.json()

            assert data["code"] == "invalid_input"
            assert "metadata" in data
            assert isinstance(data["metadata"], dict)

    @pytest.mark.asyncio
    async def test_validation_error_does_not_leak_sensitive_data(
        self, test_settings: Settings
    ) -> None:
        """Validation errors do not leak internal implementation details."""
        app = create_app(test_settings)
        transport = ASGITransport(app=app)
        async with AsyncClient(transport=transport, base_url="http://test") as client:

            @app.post("/test-validation")
            async def test_validation(body: RequestModel) -> dict[str, Any]:
                return {"ok": True}

            response = await client.post(
                "/test-validation", json={"name": "John", "age": "not_a_number"}
            )

            assert response.status_code == 400
            data = response.json()

            # Verify the response structure is safe
            assert "stack" not in data
            assert "traceback" not in data
            assert "exc_info" not in data
