"""Extended integration tests for the Query API — documents, evidence, macro,
competitive, operational, admin, agents, and analytics endpoints.

Validates endpoints not covered by test_query_api.py against the live sandbox
with deterministic seed data.  Uses the ``query_client`` and ``seed_ids``
fixtures from conftest.py.
"""

import pytest

pytestmark = pytest.mark.asyncio


# ---------------------------------------------------------------------------
# Task 2: Documents and Evidence
# ---------------------------------------------------------------------------


class TestQueryAPIDocumentFiltering:
    """Endpoints: /api/documents with ticker and document_type filters."""

    async def test_filter_documents_by_ticker(self, query_client, seed_ids):
        """GET /api/documents?ticker=AAPL — only AAPL documents."""
        resp = await query_client.get("/api/documents", params={"ticker": "AAPL"})
        assert resp.status_code == 200
        data = resp.json()
        assert len(data) >= 1
        # All returned docs should have an id field
        for doc in data:
            assert "id" in doc

    async def test_filter_documents_by_doc_type(self, query_client, seed_ids):
        """GET /api/documents?document_type=filing — only filing documents."""
        resp = await query_client.get(
            "/api/documents", params={"document_type": "filing"}
        )
        assert resp.status_code == 200
        data = resp.json()
        assert len(data) >= 1
        for doc in data:
            assert doc["document_type"] == "filing"


class TestQueryAPIDocumentDetail:
    """Endpoint: /api/documents/{id} — 404 for non-existent UUID."""

    async def test_document_not_found(self, query_client):
        """GET /api/documents/{id} — 404 for non-existent UUID."""
        fake_id = "00000000-0000-4000-ffff-000000000099"
        resp = await query_client.get(f"/api/documents/{fake_id}")
        assert resp.status_code == 404


class TestQueryAPIRecommendationEvidence:
    """Endpoint: /api/recommendations/{id}/evidence — evidence drill-down."""

    async def test_recommendation_evidence_drilldown(self, query_client, seed_ids):
        """GET /api/recommendations/{id}/evidence — evidence drill-down."""
        rec_id = seed_ids["recommendations"]["REC_01"]
        resp = await query_client.get(f"/api/recommendations/{rec_id}/evidence")
        assert resp.status_code == 200
        data = resp.json()
        assert "recommendation" in data
        assert "evidence" in data


class TestQueryAPITrendEvidence:
    """Endpoint: /api/trends/{id}/evidence — trend evidence drill-down."""

    async def test_trend_evidence_drilldown(self, query_client, seed_ids):
        """GET /api/trends/{id}/evidence — trend evidence drill-down."""
        trend_id = seed_ids["trends"]["TREND_01"]
        resp = await query_client.get(f"/api/trends/{trend_id}/evidence")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, dict)
        assert "trend" in data or "id" in data


class TestQueryAPITrendProjection:
    """Endpoint: /api/trends/{id}/projection — trend projection."""

    async def test_trend_projection(self, query_client, seed_ids):
        """GET /api/trends/{id}/projection — trend with projection."""
        trend_id = seed_ids["trends"]["TREND_01"]
        resp = await query_client.get(f"/api/trends/{trend_id}/projection")
        assert resp.status_code == 200
        data = resp.json()
        assert "projected_direction" in data
        assert "projected_strength" in data
        assert "projected_confidence" in data
        assert "macro_contribution_pct" in data

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


# ---------------------------------------------------------------------------
# Task 3: Macro and Competitive Layer
# ---------------------------------------------------------------------------


class TestQueryAPIMacro:
    """Endpoints: /api/admin/macro/status, /api/macro/events, /api/macro/impacts."""

    async def test_macro_status(self, query_client):
        """GET /api/admin/macro/status — macro layer toggle status."""
        resp = await query_client.get("/api/admin/macro/status")
        assert resp.status_code == 200
        data = resp.json()
        assert "macro_enabled" in data

    async def test_list_macro_events(self, query_client, seed_ids):
        """GET /api/macro/events — at least 2 seeded events."""
        resp = await query_client.get("/api/macro/events")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) >= 2
        for evt in data:
            assert "id" in evt
            assert "severity" in evt
            assert "summary" in evt

    async def test_get_macro_event_detail(self, query_client, seed_ids):
        """GET /api/macro/events/{id} — event detail with impacts."""
        event_id = seed_ids["global_events"]["EVT_01"]
        resp = await query_client.get(f"/api/macro/events/{event_id}")
        assert resp.status_code == 200
        data = resp.json()
        assert data["id"] == event_id
        assert "impacts" in data

    async def test_macro_impacts_by_ticker(self, query_client):
        """GET /api/macro/impacts/AAPL — at least 1 impact record."""
        resp = await query_client.get("/api/macro/impacts/AAPL")
        assert resp.status_code == 200
        data = resp.json()
        assert "impacts" in data
        assert isinstance(data["impacts"], list)
        assert len(data["impacts"]) >= 1
        for impact in data["impacts"]:
            assert "macro_impact_score" in impact
            assert "impact_direction" in impact


class TestQueryAPICompetitive:
    """Endpoints: /api/admin/competitive/status, /api/patterns/{ticker}/competitive-signals, /api/patterns/{ticker}."""

    async def test_competitive_status(self, query_client):
        """GET /api/admin/competitive/status — competitive layer toggle."""
        resp = await query_client.get("/api/admin/competitive/status")
        assert resp.status_code == 200
        data = resp.json()
        assert "competitive_enabled" in data

    async def test_competitive_signals(self, query_client):
        """GET /api/patterns/AAPL/competitive-signals — competitive signals."""
        resp = await query_client.get("/api/patterns/AAPL/competitive-signals")
        assert resp.status_code == 200
        data = resp.json()
        assert "competitive_signals" in data
        assert isinstance(data["competitive_signals"], list)

    async def test_patterns_for_ticker(self, query_client):
        """GET /api/patterns/AAPL — patterns data."""
        resp = await query_client.get("/api/patterns/AAPL")
        assert resp.status_code == 200
        data = resp.json()
        assert "ticker" in data
        assert "patterns" in data


# ---------------------------------------------------------------------------
# Task 4: Operational and Admin Endpoints
# ---------------------------------------------------------------------------


class TestQueryAPIOpsExtended:
    """Endpoints: /api/ops/pipeline/health, /api/ops/ingestion/summary, /api/ops/sources/coverage-gaps."""

    async def test_pipeline_health_fields(self, query_client):
        """GET /api/ops/pipeline/health — verify all required fields."""
        resp = await query_client.get("/api/ops/pipeline/health")
        assert resp.status_code == 200
        data = resp.json()
        assert "document_stages" in data
        assert "parsing" in data
        assert "extraction" in data
        assert "aggregation" in data
        assert "queue_depths" in data

    async def test_ingestion_summary_fields(self, query_client):
        """GET /api/ops/ingestion/summary — verify total_runs and by_source_type."""
        resp = await query_client.get("/api/ops/ingestion/summary")
        assert resp.status_code == 200
        data = resp.json()
        assert "total_runs" in data
        assert "by_source_type" in data

    async def test_coverage_gaps_fields(self, query_client):
        """GET /api/ops/sources/coverage-gaps — verify fields."""
        resp = await query_client.get("/api/ops/sources/coverage-gaps")
        assert resp.status_code == 200
        data = resp.json()
        assert "missing_source_types" in data
        assert "stale_sources" in data


class TestQueryAPISourceToggle:
    """Endpoint: PUT /api/admin/sources/{id}/toggle."""

    async def test_toggle_source(self, query_client):
        """PUT /api/admin/sources/{id}/toggle?active=false — toggle source off."""
        source_id = "00000000-0000-4000-b000-000000000001"  # SOURCE_AAPL
        resp = await query_client.put(
            f"/api/admin/sources/{source_id}/toggle", params={"active": "false"}
        )
        assert resp.status_code == 200
        # Toggle back on
        resp2 = await query_client.put(
            f"/api/admin/sources/{source_id}/toggle", params={"active": "true"}
        )
        assert resp2.status_code == 200


class TestQueryAPITradingAdmin:
    """Endpoints: /api/admin/trading/config, approvals, lockouts."""

    async def test_trading_config(self, query_client):
        """GET /api/admin/trading/config — trading config."""
        resp = await query_client.get("/api/admin/trading/config")
        assert resp.status_code == 200
        data = resp.json()
        assert "trading_mode" in data or "config" in data or "name" in data

    async def test_pending_approvals(self, query_client, seed_ids):
        """GET /api/admin/trading/approvals — pending approvals list."""
        resp = await query_client.get("/api/admin/trading/approvals")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        # Should have at least the seeded pending approval
        assert len(data) >= 1

    async def test_active_lockouts(self, query_client, seed_ids):
        """GET /api/admin/trading/lockouts — active lockouts list."""
        resp = await query_client.get("/api/admin/trading/lockouts")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) >= 1

    async def test_lockout_create_and_delete(self, query_client):
        """POST then DELETE /api/admin/trading/lockouts — lifecycle."""
        # Create
        body = {
            "ticker": "TSLA",
            "lockout_type": "manual",
            "reason": "Integration test lockout",
            "duration_minutes": 1440,
        }
        resp = await query_client.post("/api/admin/trading/lockouts", json=body)
        assert resp.status_code == 200
        data = resp.json()
        lockout_id = data["id"]
        # Delete
        resp2 = await query_client.delete(
            f"/api/admin/trading/lockouts/{lockout_id}"
        )
        assert resp2.status_code == 200


# ---------------------------------------------------------------------------
# Task 5: Agents and Analytics
# ---------------------------------------------------------------------------


class TestQueryAPIAgentsExtended:
    """Endpoints: /api/agents CRUD, variants, performance."""

    async def test_agent_detail(self, query_client, seed_ids):
        """GET /api/agents/{id} — agent detail with system_prompt, temperature, max_tokens."""
        agent_id = seed_ids["agents"]["extractor"]
        resp = await query_client.get(f"/api/agents/{agent_id}")
        assert resp.status_code == 200
        data = resp.json()
        assert "system_prompt" in data
        assert "temperature" in data
        assert "max_tokens" in data

    async def test_create_agent(self, query_client):
        """POST /api/agents — create agent with slug generation."""
        body = {
            "name": "Integration Test Agent",
            "purpose": "Testing agent creation",
            "model_provider": "ollama",
            "model_name": "qwen3.5:9b",
            "system_prompt": "You are a test agent.",
            "prompt_version": "test-v1",
            "schema_version": "1.0.0",
        }
        resp = await query_client.post("/api/agents", json=body)
        assert resp.status_code == 201
        data = resp.json()
        assert "id" in data
        assert "slug" in data
        assert data["name"] == "Integration Test Agent"

    async def test_update_agent(self, query_client, seed_ids):
        """PUT /api/agents/{id} — update agent."""
        agent_id = seed_ids["agents"]["thesis"]
        body = {"purpose": "Updated purpose for integration test"}
        resp = await query_client.put(f"/api/agents/{agent_id}", json=body)
        assert resp.status_code == 200
        data = resp.json()
        assert data["purpose"] == "Updated purpose for integration test"

    async def test_create_variant(self, query_client, seed_ids):
        """POST /api/agents/{id}/variants — create variant."""
        agent_id = seed_ids["agents"]["extractor"]
        body = {
            "variant_name": "Test Variant",
            "description": "Integration test variant",
            "model_provider": "ollama",
            "model_name": "qwen3.5:9b",
            "system_prompt": "Test variant prompt",
            "prompt_version": "test-variant-v1",
        }
        resp = await query_client.post(f"/api/agents/{agent_id}/variants", json=body)
        assert resp.status_code == 201
        data = resp.json()
        assert "id" in data
        assert data["variant_name"] == "Test Variant"

    async def test_activate_variant(self, query_client, seed_ids):
        """POST /api/agents/{id}/variants/{vid}/activate — activate variant."""
        agent_id = seed_ids["agents"]["extractor"]
        variant_id = seed_ids["variants"]["extractor"]
        resp = await query_client.post(
            f"/api/agents/{agent_id}/variants/{variant_id}/activate"
        )
        assert resp.status_code == 200

    async def test_agent_performance(self, query_client, seed_ids):
        """GET /api/agents/{id}/performance — agent performance metrics."""
        agent_id = seed_ids["agents"]["extractor"]
        resp = await query_client.get(f"/api/agents/{agent_id}/performance")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, dict)

    async def test_variant_performance(self, query_client, seed_ids):
        """GET /api/agents/{id}/variants/{vid}/performance — variant performance."""
        agent_id = seed_ids["agents"]["extractor"]
        variant_id = seed_ids["variants"]["extractor"]
        resp = await query_client.get(
            f"/api/agents/{agent_id}/variants/{variant_id}/performance"
        )
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, dict)


class TestQueryAPIAnalytics:
    """Endpoints: /api/analytics/pg-schema, saved-queries, pg-query."""

    async def test_pg_schema(self, query_client):
        """GET /api/analytics/pg-schema — PG schema info."""
        resp = await query_client.get("/api/analytics/pg-schema")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, (list, dict))

    async def test_list_saved_queries(self, query_client, seed_ids):
        """GET /api/analytics/saved-queries — at least 1 from seed."""
        resp = await query_client.get("/api/analytics/saved-queries")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) >= 1

    async def test_saved_query_create_and_delete(self, query_client):
        """POST then DELETE /api/analytics/saved-queries — lifecycle."""
        body = {
            "name": "IntTest Query",
            "description": "Integration test saved query",
            "sql_text": "SELECT 1 AS test",
        }
        resp = await query_client.post("/api/analytics/saved-queries", json=body)
        assert resp.status_code == 201
        data = resp.json()
        query_id = data["id"]
        # Delete
        resp2 = await query_client.delete(f"/api/analytics/saved-queries/{query_id}")
        assert resp2.status_code == 200

    async def test_pg_query(self, query_client):
        """POST /api/analytics/pg-query — valid read-only SQL."""
        body = {"sql": "SELECT ticker, legal_name FROM companies LIMIT 5"}
        resp = await query_client.post("/api/analytics/pg-query", json=body)
        assert resp.status_code == 200
        data = resp.json()
        assert "columns" in data or "rows" in data or isinstance(data, list)
