"""
Tests for workspace and ingest REST endpoints.

These exercise serializer validation, idempotency rules, and Django ORM
behavior for IngestJob. The Cypher-touching paths (Library node CRUD,
search scoping) require Neo4j and are validated by the manual end-to-end
test plan, not these unit tests.
"""

from django.test import TestCase
from rest_framework.test import APIClient

from library.api.serializers import (
    IngestRequestSerializer,
    WorkspaceCreateSerializer,
)
from library.models import IngestJob


class WorkspaceCreateSerializerTests(TestCase):
    """Validation rules for the create-workspace payload."""

    def test_minimal_payload_validates(self):
        s = WorkspaceCreateSerializer(
            data={
                "workspace_id": "ws_abc",
                "name": "My Workspace",
                "library_type": "technical",
            }
        )
        self.assertTrue(s.is_valid(), s.errors)

    def test_business_type_accepted(self):
        s = WorkspaceCreateSerializer(
            data={
                "workspace_id": "ws_abc",
                "name": "Sales",
                "library_type": "business",
            }
        )
        self.assertTrue(s.is_valid(), s.errors)

    def test_finance_type_accepted(self):
        s = WorkspaceCreateSerializer(
            data={
                "workspace_id": "ws_abc",
                "name": "Money",
                "library_type": "finance",
            }
        )
        self.assertTrue(s.is_valid(), s.errors)

    def test_unknown_type_rejected(self):
        s = WorkspaceCreateSerializer(
            data={
                "workspace_id": "ws_abc",
                "name": "x",
                "library_type": "miscellaneous",
            }
        )
        self.assertFalse(s.is_valid())
        self.assertIn("library_type", s.errors)

    def test_workspace_id_required(self):
        s = WorkspaceCreateSerializer(
            data={"name": "x", "library_type": "technical"}
        )
        self.assertFalse(s.is_valid())
        self.assertIn("workspace_id", s.errors)


class IngestRequestSerializerTests(TestCase):
    """Validation rules for the ingest payload."""

    BASE_PAYLOAD = {
        "s3_key": "workspaces/ws/files/f/x.pdf",
        "title": "Q4 Report",
        "content_hash": "a" * 64,
    }

    def test_workspace_id_only_validates(self):
        s = IngestRequestSerializer(
            data={**self.BASE_PAYLOAD, "workspace_id": "ws_abc"}
        )
        self.assertTrue(s.is_valid(), s.errors)

    def test_library_uid_only_validates(self):
        s = IngestRequestSerializer(
            data={**self.BASE_PAYLOAD, "library_uid": "lib_xyz"}
        )
        self.assertTrue(s.is_valid(), s.errors)

    def test_neither_workspace_nor_library_rejected(self):
        s = IngestRequestSerializer(data=self.BASE_PAYLOAD)
        self.assertFalse(s.is_valid())
        # ValidationError on the whole payload, not a specific field
        self.assertIn("non_field_errors", s.errors)

    def test_content_hash_required(self):
        s = IngestRequestSerializer(
            data={
                "s3_key": "x",
                "title": "y",
                "workspace_id": "ws_abc",
            }
        )
        self.assertFalse(s.is_valid())
        self.assertIn("content_hash", s.errors)


class IngestJobModelTests(TestCase):
    """IngestJob persists and queries correctly."""

    def test_create_with_minimal_fields(self):
        job = IngestJob.objects.create(
            id="job_test1",
            library_uid="lib_xyz",
            s3_key="x.pdf",
        )
        self.assertEqual(job.status, "pending")
        self.assertEqual(job.progress, "queued")
        self.assertEqual(job.retry_count, 0)
        self.assertEqual(job.chunks_created, 0)

    def test_idempotency_query_pattern(self):
        """The idempotency query in ingest_create uses (library, source_ref, hash)."""
        IngestJob.objects.create(
            id="job_a",
            library_uid="lib_xyz",
            source_ref="ws_a/file_1",
            content_hash="h1",
            s3_key="a.pdf",
            status="completed",
        )
        IngestJob.objects.create(
            id="job_b",
            library_uid="lib_xyz",
            source_ref="ws_a/file_1",
            content_hash="h2",  # different hash — supersedes
            s3_key="a.pdf",
            status="completed",
        )

        # Same library + source_ref + h1 → finds job_a
        match = IngestJob.objects.filter(
            library_uid="lib_xyz",
            source_ref="ws_a/file_1",
            content_hash="h1",
        ).first()
        self.assertIsNotNone(match)
        self.assertEqual(match.id, "job_a")

        # h2 → finds job_b
        match = IngestJob.objects.filter(
            library_uid="lib_xyz",
            source_ref="ws_a/file_1",
            content_hash="h2",
        ).first()
        self.assertEqual(match.id, "job_b")

        # Different source_ref → no match
        match = IngestJob.objects.filter(
            library_uid="lib_xyz",
            source_ref="ws_a/file_2",
            content_hash="h1",
        ).first()
        self.assertIsNone(match)


class IngestEndpointAuthTests(TestCase):
    """Auth boundary on the ingest endpoint (matches the existing convention)."""

    def setUp(self):
        self.client = APIClient()

    def test_ingest_requires_auth(self):
        response = self.client.post(
            "/library/api/ingest/",
            {
                "s3_key": "x",
                "title": "y",
                "workspace_id": "ws_abc",
                "content_hash": "a" * 64,
            },
            format="json",
        )
        self.assertIn(response.status_code, [401, 403])


class WorkspaceEndpointAuthTests(TestCase):
    """Auth on workspace endpoints."""

    def setUp(self):
        self.client = APIClient()

    def test_workspace_create_requires_auth(self):
        response = self.client.post(
            "/library/api/workspaces/",
            {"workspace_id": "ws_a", "name": "x", "library_type": "technical"},
            format="json",
        )
        self.assertIn(response.status_code, [401, 403])

    def test_workspace_get_requires_auth(self):
        response = self.client.get("/library/api/workspaces/ws_a/")
        self.assertIn(response.status_code, [401, 403])

    def test_workspace_delete_requires_auth(self):
        response = self.client.delete("/library/api/workspaces/ws_a/")
        self.assertIn(response.status_code, [401, 403])
