"""Tests for Mock OA helper endpoints.

Covers the following /api/v1/mock/* endpoints that were previously untested:
  - GET  /mock/users  — list predefined test users
  - GET  /mock/docs   — list documents in ES
  - GET  /mock/task/{task_id} — check Celery task status
  - DELETE /mock/doc/{doc_id}  — delete test document
  - GET  /mock/stats  — system stats (ES, Redis, Neo4j)
"""

import pytest
from httpx import AsyncClient


@pytest.mark.asyncio
class TestMockUsers:
    """GET /api/v1/mock/users — list predefined test users."""

    async def test_list_users_returns_200(self, client: AsyncClient, api_prefix: str):
        """Endpoint should return a list of test users (no auth required)."""
        resp = await client.get(f"{api_prefix}/mock/users")
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)
        assert len(body) >= 4  # admin, zhang_san, li_si, wang_wu

    async def test_list_users_fields(self, client: AsyncClient, api_prefix: str):
        """Each user should have the expected fields."""
        resp = await client.get(f"{api_prefix}/mock/users")
        body = resp.json()
        for user in body:
            assert "username" in user
            assert "display_name" in user
            assert "office_id" in user
            assert "dept_id" in user
            assert "area_id" in user
            assert "role_ids" in user
            assert "role" in user

    async def test_list_users_no_passwords(self, client: AsyncClient, api_prefix: str):
        """User list should NOT include passwords."""
        resp = await client.get(f"{api_prefix}/mock/users")
        body = resp.json()
        for user in body:
            assert "password" not in user

    async def test_known_users_present(self, client: AsyncClient, api_prefix: str):
        """Expected test users should be present."""
        resp = await client.get(f"{api_prefix}/mock/users")
        usernames = [u["username"] for u in resp.json()]
        for expected in ("admin", "zhang_san", "li_si", "wang_wu"):
            assert expected in usernames, f"Missing expected user: {expected}"


@pytest.mark.asyncio
class TestMockDocs:
    """GET /api/v1/mock/docs — list documents in ES (no auth)."""

    async def test_docs_returns_200(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/mock/docs")
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, list)

    async def test_docs_with_size_param(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(
            f"{api_prefix}/mock/docs",
            params={"size": 5},
        )
        assert resp.status_code == 200
        body = resp.json()
        assert len(body) <= 5

    async def test_docs_with_keyword(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(
            f"{api_prefix}/mock/docs",
            params={"keyword": "数字政府"},
        )
        assert resp.status_code == 200
        assert isinstance(resp.json(), list)

    async def test_docs_item_fields(self, client: AsyncClient, api_prefix: str):
        """If there are documents, each item should have the expected fields."""
        resp = await client.get(f"{api_prefix}/mock/docs")
        body = resp.json()
        if body:  # Only check if there are docs
            item = body[0]
            expected_fields = {"doc_id", "title", "doc_type", "status"}
            assert expected_fields <= set(item.keys()), f"Missing fields in doc item: {expected_fields - set(item.keys())}"


@pytest.mark.asyncio
class TestMockTaskStatus:
    """GET /api/v1/mock/task/{task_id} — Celery task status (no auth)."""

    async def test_task_status_returns_200(self, client: AsyncClient, api_prefix: str):
        """Even a non-existent task should return 200 with PENDING status."""
        resp = await client.get(f"{api_prefix}/mock/task/nonexistent-task-id-xyz")
        assert resp.status_code == 200
        body = resp.json()
        assert body["task_id"] == "nonexistent-task-id-xyz"
        assert body["status"] == "PENDING"

    async def test_task_status_response_fields(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/mock/task/fake-task-abc")
        body = resp.json()
        assert "task_id" in body
        assert "status" in body
        assert "progress" in body
        assert isinstance(body["progress"], (int, float))


@pytest.mark.asyncio
class TestMockDeleteDoc:
    """DELETE /api/v1/mock/doc/{doc_id} — delete test document (no auth)."""

    async def test_delete_nonexistent_doc(self, client: AsyncClient, api_prefix: str):
        """Deleting a nonexistent document should not crash (may return various codes)."""
        resp = await client.delete(f"{api_prefix}/mock/doc/nonexistent_mock_doc_xyz")
        # Could be 200 (with no-op result) or 404 or 500 depending on ES state
        assert resp.status_code in (200, 404, 500)


@pytest.mark.asyncio
class TestMockStats:
    """GET /api/v1/mock/stats — system stats overview (no auth)."""

    async def test_stats_returns_200(self, client: AsyncClient, api_prefix: str):
        resp = await client.get(f"{api_prefix}/mock/stats")
        assert resp.status_code == 200
        body = resp.json()
        assert isinstance(body, dict)

    async def test_stats_contains_services(self, client: AsyncClient, api_prefix: str):
        """Stats should include ES, Redis, Neo4j info."""
        resp = await client.get(f"{api_prefix}/mock/stats")
        body = resp.json()
        # Should contain info about at least one service
        assert any(key in body for key in ("es", "redis", "neo4j")), (
            f"Stats missing service info. Keys: {list(body.keys())}"
        )
