"""Performance regression tests: count annotations + prefetch reuse stay O(1) queries."""

from datetime import date

import pytest
from django.contrib.auth import get_user_model
from django.db import connection
from django.test.utils import CaptureQueriesContext
from rest_framework.test import APIClient

from apps.administrations.models import DocumentRequest, LetterNumber, LetterPrefix, SOP, SOPCategory

User = get_user_model()


@pytest.fixture
def client(db):
    admin = User.objects.create_superuser(email="admin@test.local", password="x")
    c = APIClient()
    c.force_authenticate(admin)
    c.admin = admin
    return c


@pytest.fixture
def organization(client):
    # create_superuser auto-provisions its own Organization; reuse it so the
    # client's single membership matches get_active_organization's pick.
    return client.admin.organizations.get()


def _make_letter(organization, prefix, request=None, seq=1):
    return LetterNumber.objects.create(
        organization=organization, prefix=prefix, year=2026, month=6, sequence=seq,
        subject=f"S{seq}", recipient="R", sender="X", date=date(2026, 6, seq),
        document_request=request,
    )


def _rows(resp):
    body = resp.json()
    return body["results"] if isinstance(body, dict) and "results" in body else body


@pytest.mark.django_db
class TestLetterPrefixCounts:
    def test_counts_correct(self, client, organization):
        a = LetterPrefix.objects.create(organization=organization, code="A", label="A dept")
        b = LetterPrefix.objects.create(organization=organization, code="B", label="B dept")
        _make_letter(organization, a, seq=1)
        _make_letter(organization, a, seq=2)

        rows = {r["code"]: r for r in _rows(client.get("/api/letter-prefixes/"))}
        assert rows["A"]["letter_count"] == 2
        assert rows["B"]["letter_count"] == 0

    def test_queries_constant_in_rows(self, client, organization):
        for i in range(2):
            LetterPrefix.objects.create(organization=organization, code=f"P{i}", label=f"P{i}")
        with CaptureQueriesContext(connection) as small:
            client.get("/api/letter-prefixes/")
        for i in range(2, 8):
            p = LetterPrefix.objects.create(organization=organization, code=f"P{i}", label=f"P{i}")
            _make_letter(organization, p, seq=i)
        with CaptureQueriesContext(connection) as big:
            client.get("/api/letter-prefixes/")
        assert len(big.captured_queries) == len(small.captured_queries)


@pytest.mark.django_db
class TestSOPCategoryCounts:
    def test_counts_correct_including_empty(self, client, organization):
        cat = SOPCategory.objects.create(organization=organization, name="Ops")
        SOPCategory.objects.create(organization=organization, name="Empty")
        author = User.objects.create_user(email="a@test.local", password="x")
        for i in range(3):
            SOP.objects.create(organization=organization, code=f"SOP-{i}", title=f"T{i}", category="Ops",
                               content="c", author=author, effective_date=date(2026, 1, 1))

        rows = {r["name"]: r for r in _rows(client.get("/api/sop-categories/"))}
        assert rows["Ops"]["sop_count"] == 3
        assert rows["Empty"]["sop_count"] == 0  # Coalesce path, not a fallback query

    def test_queries_constant_in_rows(self, client, organization):
        author = User.objects.create_user(email="a@test.local", password="x")
        SOPCategory.objects.create(organization=organization, name="C0")
        with CaptureQueriesContext(connection) as small:
            client.get("/api/sop-categories/")
        for i in range(1, 7):
            SOPCategory.objects.create(organization=organization, name=f"C{i}")
            SOP.objects.create(organization=organization, code=f"SOP-{i}", title="T", category=f"C{i}",
                               content="c", author=author, effective_date=date(2026, 1, 1))
        with CaptureQueriesContext(connection) as big:
            client.get("/api/sop-categories/")
        assert len(big.captured_queries) == len(small.captured_queries)


@pytest.mark.django_db
class TestDocumentRequestLatestLetter:
    def test_latest_letter_fields(self, client, organization):
        requester = User.objects.create_user(email="r@test.local", password="x")
        prefix = LetterPrefix.objects.create(organization=organization, code="DOC", label="Docs")
        req = DocumentRequest.objects.create(
            organization=organization, requester=requester, document_type="permit", purpose="p")
        _make_letter(organization, prefix, request=req, seq=1)
        latest = _make_letter(organization, prefix, request=req, seq=2)

        rows = _rows(client.get("/api/document-requests/"))
        row = rows[0]
        # uuid7's str() omits dashes; normalize both sides before comparing.
        assert row["letter_id"].replace("-", "") == str(latest.id).replace("-", "")
        assert row["letter_number"] == latest.full_number

    def test_no_letters_yet(self, client, organization):
        requester = User.objects.create_user(email="r@test.local", password="x")
        DocumentRequest.objects.create(
            organization=organization, requester=requester, document_type="permit", purpose="p")
        row = _rows(client.get("/api/document-requests/"))[0]
        assert row["letter_number"] is None
        assert row["letter_id"] is None

    def test_queries_constant_in_rows(self, client, organization):
        requester = User.objects.create_user(email="r@test.local", password="x")
        prefix = LetterPrefix.objects.create(organization=organization, code="DOC", label="Docs")
        req = DocumentRequest.objects.create(
            organization=organization, requester=requester, document_type="permit", purpose="p")
        _make_letter(organization, prefix, request=req, seq=1)
        with CaptureQueriesContext(connection) as small:
            client.get("/api/document-requests/")
        for i in range(2, 7):
            r = DocumentRequest.objects.create(
                organization=organization, requester=requester, document_type=f"doc{i}", purpose="p")
            _make_letter(organization, prefix, request=r, seq=i)
        with CaptureQueriesContext(connection) as big:
            client.get("/api/document-requests/")
        assert len(big.captured_queries) == len(small.captured_queries)


@pytest.mark.django_db
class TestLetterPrefixPaginationOrdering:
    """BE-005: LetterPrefixViewSet's queryset previously ordered by `code`
    only, which isn't unique — pagination across duplicate-code rows (across
    orgs, since uniqueness is per-org) could warn/reorder/duplicate. `pk` is
    now the tie-breaker."""

    def test_no_unordered_pagination_warning(self, client, organization):
        LetterPrefix.objects.create(organization=organization, code="A", label="Alpha")
        LetterPrefix.objects.create(organization=organization, code="B", label="Beta")
        import warnings
        from django.core.paginator import UnorderedObjectListWarning

        with warnings.catch_warnings():
            warnings.simplefilter("error", UnorderedObjectListWarning)
            resp = client.get("/api/letter-prefixes/")
        assert resp.status_code == 200

    def test_stable_across_identical_pages(self, client, organization):
        # `code` is unique per org, so use several rows that all sort into
        # the same position under the old code-only ordering (ties on the
        # first several characters truncated by a shared prefix aren't
        # possible with a plain CharField sort) — instead assert stability
        # directly against repeated identical requests.
        for i in range(3):
            LetterPrefix.objects.create(organization=organization, code=f"C{i}", label=f"L{i}")
        first = _rows(client.get("/api/letter-prefixes/"))
        second = _rows(client.get("/api/letter-prefixes/"))
        assert [r["id"] for r in first] == [r["id"] for r in second]
