"""Integration tests for error handling and empty-state behavior across all services.

Validates that all endpoints return structured JSON errors (not HTML or stack
traces) and handle empty-state gracefully, so the frontend never receives
unexpected response formats.
"""

import pytest

pytestmark = pytest.mark.asyncio


# ---------------------------------------------------------------------------
# Empty list endpoints
# ---------------------------------------------------------------------------


class TestEmptyListEndpoints:
    """List endpoints with no matching data return 200 with []."""

    async def test_documents_empty_filter(self, query_client):
        """GET /api/documents?ticker=ZZZZ — no matching docs returns 200 with list."""
        resp = await query_client.get("/api/documents", params={"ticker": "ZZZZ"})
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) == 0


# ---------------------------------------------------------------------------
# Not-found detail endpoints
# ---------------------------------------------------------------------------


class TestNotFoundEndpoints:
    """Detail endpoints with non-existent UUID return 404 with JSON body."""

    async def test_query_api_document_not_found(self, query_client):
        """GET /api/documents/{id} — 404 with JSON body."""
        fake_id = "00000000-0000-4000-ffff-000000000099"
        resp = await query_client.get(f"/api/documents/{fake_id}")
        assert resp.status_code == 404
        data = resp.json()
        assert isinstance(data, dict)

    async def test_registry_company_not_found(self, registry_client):
        """GET /companies/{id} — 404 with JSON body."""
        fake_id = "00000000-0000-4000-ffff-000000000099"
        resp = await registry_client.get(f"/companies/{fake_id}")
        assert resp.status_code == 404
        data = resp.json()
        assert isinstance(data, dict)

    async def test_risk_approval_not_found(self, risk_client):
        """GET /approvals/{id} — 404 with JSON body."""
        fake_id = "00000000-0000-4000-ffff-000000000099"
        resp = await risk_client.get(f"/approvals/{fake_id}")
        assert resp.status_code == 404
        data = resp.json()
        assert isinstance(data, dict)


# ---------------------------------------------------------------------------
# Validation errors
# ---------------------------------------------------------------------------


class TestValidationErrors:
    """Invalid JSON body returns 422 with validation details."""

    async def test_invalid_company_body(self, registry_client):
        """POST /companies — missing required fields returns 422."""
        resp = await registry_client.post("/companies", json={})
        assert resp.status_code == 422
        data = resp.json()
        assert isinstance(data, dict)
        assert "detail" in data


# ---------------------------------------------------------------------------
# Trend projection edge case
# ---------------------------------------------------------------------------


class TestTrendProjectionNoProjection:
    """Trend projection with no projection returns appropriate response (not 500)."""

    async def test_trend_projection_nonexistent(self, query_client):
        """GET /api/trends/{id}/projection — non-existent trend returns 404, not 500."""
        fake_id = "00000000-0000-4000-ffff-000000000099"
        resp = await query_client.get(f"/api/trends/{fake_id}/projection")
        assert resp.status_code != 500
        assert resp.status_code in (200, 404)


# ---------------------------------------------------------------------------
# Macro impacts empty state
# ---------------------------------------------------------------------------


class TestMacroImpactsEmpty:
    """Macro impacts with no data returns 200 with empty impacts list."""

    async def test_macro_impacts_no_data(self, query_client):
        """GET /api/macro/impacts/ZZZZ — no impacts returns 200 with empty list."""
        resp = await query_client.get("/api/macro/impacts/ZZZZ")
        assert resp.status_code == 200
        data = resp.json()
        assert "impacts" in data
        assert isinstance(data["impacts"], list)
        assert len(data["impacts"]) == 0


# ---------------------------------------------------------------------------
# Health endpoints across all services
# ---------------------------------------------------------------------------


class TestAllServiceHealthEndpoints:
    """All four service health endpoints return {"status": "ok"}."""

    async def test_query_api_health(self, query_client):
        """GET /health — query API."""
        resp = await query_client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["status"] == "ok"

    async def test_registry_health(self, registry_client):
        """GET /health — symbol registry."""
        resp = await registry_client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["status"] == "ok"

    async def test_risk_health(self, risk_client):
        """GET /health — risk engine."""
        resp = await risk_client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["status"] == "ok"

    async def test_trading_health(self, trading_client):
        """GET /health — trading engine."""
        resp = await trading_client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["status"] == "ok"


# ---------------------------------------------------------------------------
# Override order structured error
# ---------------------------------------------------------------------------


class TestOverrideStructuredError:
    """Override order returns structured JSON error when Redis unavailable."""

    async def test_override_structured_error(self, trading_client):
        """POST /api/trading/override/order — structured error (not unhandled exception)."""
        payload = {
            "ticker": "AAPL",
            "side": "buy",
            "quantity": 1.0,
            "order_type": "market",
        }
        resp = await trading_client.post("/api/trading/override/order", json=payload)
        # Accept 202 (success) or structured error
        assert resp.status_code in (200, 202, 400, 422, 503)
        data = resp.json()
        assert isinstance(data, dict)
        # If 503, verify it's JSON not an unhandled exception
        if resp.status_code == 503:
            assert "detail" in data or "error" in data or "message" in data
