"""Business flow tests: Permission-based access control.

Tests that different users see different documents based on their ACL tokens.

Flow:
  1. Ingest a document with restricted ACL (e.g., only dept D_05)
  2. User in D_05 can search and see it
  3. User in D_08 cannot see it in search results
  4. After ACL update (add D_08), the second user can see it

Requires running Docker infra + Celery worker.
"""

from __future__ import annotations

import asyncio
import json
import time
from pathlib import Path

import pytest
from httpx import AsyncClient

_INGEST_TIMEOUT = 180
_POLL_INTERVAL = 3


@pytest.mark.asyncio
class TestPermissionFlow:
    """Permission-based document visibility."""

    async def _login_as(self, client: AsyncClient, api_prefix: str, username: str, password: str) -> str:
        resp = await client.post(f"{api_prefix}/mock/login", json={
            "username": username,
            "password": password,
        })
        assert resp.status_code == 200
        return resp.json()["access_token"]

    async def _wait_ingest(self, client: AsyncClient, api_prefix: str, task_id: str, headers: dict) -> dict:
        start = time.time()
        while time.time() - start < _INGEST_TIMEOUT:
            resp = await client.get(f"{api_prefix}/ingest/status/{task_id}", headers=headers)
            body = resp.json()
            if body.get("status") in ("COMPLETED", "FAILED"):
                return body
            await asyncio.sleep(_POLL_INTERVAL)
        pytest.fail(f"Ingest timeout for task {task_id}")

    async def test_acl_restricts_visibility(
        self,
        client: AsyncClient,
        api_prefix: str,
        example_pdf_path: Path,
    ):
        """Document with ACL=[D_05] should be visible to zhang_san (D_05)
        but not to li_si (D_08).
        """
        # Step 1: Login as admin to ingest
        admin_token = await self._login_as(client, api_prefix, "admin", "admin123")
        admin_headers = {"Authorization": f"Bearer {admin_token}"}

        doc_id = f"acl-test-{int(time.time())}"

        # Step 2: Ingest with restricted ACL (only D_05)
        meta = json.dumps({
            "doc_id": doc_id,
            "title": "ACL权限测试专用文档",
            "issuing_org": "权限测试单位",
            "doc_type": "通知",
            "acl_ids": ["D_05"],  # Only dept D_05 can see this
        })
        with open(example_pdf_path, "rb") as f:
            resp = await client.post(
                f"{api_prefix}/ingest/webhook/document",
                data={"metadata": meta},
                files={"file": (example_pdf_path.name, f, "application/pdf")},
            )
        assert resp.status_code == 200
        task_id = resp.json()["task_id"]

        result = await self._wait_ingest(client, api_prefix, task_id, admin_headers)
        assert result["status"] == "COMPLETED", f"Ingest failed: {result.get('error')}"

        await asyncio.sleep(2)  # Wait for index refresh

        # Step 3: zhang_san (D_05) should see the document
        zhang_token = await self._login_as(client, api_prefix, "zhang_san", "user123")
        zhang_headers = {"Authorization": f"Bearer {zhang_token}"}

        resp = await client.get(
            f"{api_prefix}/document/{doc_id}",
            headers=zhang_headers,
        )
        # zhang_san is in D_05, should have access
        assert resp.status_code == 200, f"zhang_san should see doc: {resp.status_code}"

        # Step 4: li_si (D_08) should NOT see the document
        li_token = await self._login_as(client, api_prefix, "li_si", "user123")
        li_headers = {"Authorization": f"Bearer {li_token}"}

        resp = await client.get(
            f"{api_prefix}/document/{doc_id}",
            headers=li_headers,
        )
        # li_si is in D_08, should be denied
        assert resp.status_code in (403, 404), f"li_si should NOT see doc: {resp.status_code}"

        # Step 5: Clean up
        await client.delete(
            f"{api_prefix}/admin/document/{doc_id}",
            headers=admin_headers,
        )
