"""Tests for the Graph (knowledge graph explorer) API endpoints.

Covers:
  - Graph overview, search, entity detail, related docs
  - Phase 0 governance endpoints: recommendations, policy-chain,
    revision-history, same-theme
"""

import pytest
from httpx import AsyncClient


@pytest.mark.asyncio
class TestGraphOverview:
    """GET /api/v1/graph/overview — global graph stats."""

    async def test_overview(self, client: AsyncClient, api_prefix: str, auth_headers: dict):
        resp = await client.get(
            f"{api_prefix}/graph/overview",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert "nodes" in body or "total_nodes" in body or isinstance(body, dict)


@pytest.mark.asyncio
class TestGraphSearch:
    """POST /api/v1/graph/search — entity search."""

    async def test_search_requires_auth(self, client: AsyncClient, api_prefix: str):
        resp = await client.post(f"{api_prefix}/graph/search", json={
            "name": "数字政府",
        })
        assert resp.status_code in (401, 403)

    async def test_search_basic(self, client: AsyncClient, api_prefix: str, auth_headers: dict):
        resp = await client.post(
            f"{api_prefix}/graph/search",
            json={"name": "广东"},
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)


@pytest.mark.asyncio
class TestGraphEntity:
    """GET /api/v1/graph/entity/{id} — entity detail + neighborhood."""

    async def test_entity_nonexistent(self, client: AsyncClient, api_prefix: str, auth_headers: dict):
        resp = await client.get(
            f"{api_prefix}/graph/entity/99999999",
            headers=auth_headers,
        )
        # Could be 200 with empty or 404
        assert resp.status_code in (200, 404)


@pytest.mark.asyncio
class TestGraphRelatedDocs:
    """POST /api/v1/graph/related-docs — find docs related to entities."""

    async def test_related_docs(self, client: AsyncClient, api_prefix: str, auth_headers: dict):
        resp = await client.post(
            f"{api_prefix}/graph/related-docs",
            json={
                "entity_name": "数字政府",
                "entity_label": "PolicyTheme",
            },
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)


@pytest.mark.asyncio
class TestGraphDocumentEntities:
    """GET /api/v1/graph/document/{doc_id}/entities — entities for a document."""

    async def test_doc_entities(self, client: AsyncClient, api_prefix: str, auth_headers: dict):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/entities",
            headers=auth_headers,
        )
        assert resp.status_code in (200, 404)


# ===========================================================================
# Phase 0 governance endpoints
# ===========================================================================


@pytest.mark.asyncio
class TestDocumentRecommendations:
    """GET /api/v1/graph/document/{doc_id}/recommendations."""

    async def test_recommendations_returns_200(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/recommendations",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, dict)
        # Should return same_org, same_theme, same_region keys
        assert "same_org" in body
        assert "same_theme" in body
        assert "same_region" in body

    async def test_recommendations_result_structure(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/recommendations?limit=5",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        for key in ("same_org", "same_theme", "same_region"):
            assert isinstance(body[key], list)

    async def test_recommendations_requires_auth(
        self, client: AsyncClient, api_prefix: str
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/any-doc/recommendations",
        )
        assert resp.status_code in (401, 403)


@pytest.mark.asyncio
class TestPolicyChain:
    """GET /api/v1/graph/document/{doc_id}/policy-chain."""

    async def test_policy_chain_returns_200(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/policy-chain",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, dict)
        assert "chain" in body
        assert "edges" in body

    async def test_policy_chain_empty_for_nonexistent(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/no-such-doc/policy-chain",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert body["chain"] == []
        assert body["edges"] == []

    async def test_policy_chain_max_depth(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/any-doc/policy-chain?max_depth=3",
            headers=auth_headers,
        )
        assert resp.status_code == 200


@pytest.mark.asyncio
class TestRevisionHistory:
    """GET /api/v1/graph/document/{doc_id}/revision-history."""

    async def test_revision_history_returns_200(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/revision-history",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, dict)
        assert "documents" in body
        assert "edges" in body

    async def test_revision_history_empty_for_nonexistent(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/no-such-doc/revision-history",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert body["documents"] == []
        assert body["edges"] == []


@pytest.mark.asyncio
class TestSameTheme:
    """GET /api/v1/graph/document/{doc_id}/same-theme."""

    async def test_same_theme_returns_200(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/nonexistent-doc/same-theme",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)

    async def test_same_theme_with_limit(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/document/any-doc/same-theme?limit=5",
            headers=auth_headers,
        )
        assert resp.status_code == 200


# ===========================================================================
# Phase 1: Matter endpoints
# ===========================================================================


@pytest.mark.asyncio
class TestMatterSearch:
    """GET /api/v1/graph/matters — matter search."""

    async def test_search_allows_anonymous_access(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/graph/matters?query=审批")
        assert resp.status_code == 200

    async def test_search_returns_200(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/matters?query=审批",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)

    async def test_search_empty_query_returns_empty(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/matters?query=",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        assert resp.json() == []

    async def test_search_nonexistent_returns_empty_list(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/matters?query=不存在的事项xyz",
            headers=auth_headers,
        )
        assert resp.status_code == 200
        assert resp.json() == []


@pytest.mark.asyncio
class TestMatterCard:
    """GET /api/v1/graph/matters/{matter_id} — matter knowledge card."""

    async def test_nonexistent_returns_404(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/matters/nonexistent_matter_id",
            headers=auth_headers,
        )
        assert resp.status_code == 404

    async def test_anonymous_request_hits_public_endpoint(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/graph/matters/any_id")
        assert resp.status_code == 404


@pytest.mark.asyncio
class TestMatterRequirements:
    """GET /api/v1/graph/matters/{matter_id}/requirements — matter requirements."""

    async def test_nonexistent_returns_404(
        self, client: AsyncClient, api_prefix: str, auth_headers: dict
    ):
        resp = await client.get(
            f"{api_prefix}/graph/matters/nonexistent_matter_id/requirements",
            headers=auth_headers,
        )
        assert resp.status_code == 404

    async def test_anonymous_request_hits_public_endpoint(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/graph/matters/any_id/requirements")
        assert resp.status_code == 404
