"""Security tests for the gated document download path.

These are the tests that matter most: a logged-out request must never receive a
file, a file must have no public URL, drafts are staff-only, and every download
is logged. They exercise the real view through Django's test client and write
files into a throwaway temp dir (not the real document store).
"""
import shutil
import tempfile

from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse

from documents.models import Category, Document, DownloadLog

User = get_user_model()
_TMP_ROOT = tempfile.mkdtemp(prefix="hartling-docs-test-")

SECRET_BYTES = b"%PDF-1.4 confidential AGM financials"


@override_settings(PRIVATE_MEDIA_ROOT=_TMP_ROOT)
class DocumentDownloadTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.owner = User.objects.create_user(username="owner", password="pw-owner-123")
        cls.staff = User.objects.create_user(
            username="staff", password="pw-staff-123", is_staff=True
        )
        cls.category = Category.objects.create(name="Minutes", slug="minutes")
        cls.doc = Document.objects.create(
            title="AGM 2026 Minutes",
            category=cls.category,
            published=True,
            file=SimpleUploadedFile("agm.pdf", SECRET_BYTES, content_type="application/pdf"),
        )
        cls.draft = Document.objects.create(
            title="Draft Budget",
            published=False,
            file=SimpleUploadedFile("draft.pdf", b"%PDF draft", content_type="application/pdf"),
        )

    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(_TMP_ROOT, ignore_errors=True)
        super().tearDownClass()

    def _url(self, doc):
        return reverse("documents:download", args=[doc.pk])

    def test_anonymous_is_redirected_to_login_and_gets_no_file(self):
        response = self.client.get(self._url(self.doc))
        self.assertEqual(response.status_code, 302)
        self.assertIn("/accounts/login/", response.url)

    def test_authenticated_owner_gets_file_with_private_headers(self):
        self.client.force_login(self.owner)
        response = self.client.get(self._url(self.doc))
        self.assertEqual(response.status_code, 200)
        self.assertEqual(b"".join(response.streaming_content), SECRET_BYTES)
        self.assertIn("noindex", response["X-Robots-Tag"])
        self.assertIn("attachment", response["Content-Disposition"])
        self.assertIn("no-store", response["Cache-Control"])
        self.assertEqual(response["X-Content-Type-Options"], "nosniff")

    def test_each_download_is_logged(self):
        self.client.force_login(self.owner)
        self.client.get(self._url(self.doc))
        log = DownloadLog.objects.get(user=self.owner, document=self.doc)
        self.assertEqual(log.which, "primary")

    def test_draft_hidden_from_owner_but_visible_to_staff(self):
        self.client.force_login(self.owner)
        self.assertEqual(self.client.get(self._url(self.draft)).status_code, 404)
        self.client.force_login(self.staff)
        self.assertEqual(self.client.get(self._url(self.draft)).status_code, 200)

    def test_stored_file_has_no_public_url(self):
        # The single most important property: a document has no URL that works
        # without the view. Private storage has no base_url, so .url() raises.
        with self.assertRaises(ValueError):
            _ = self.doc.file.url

    def test_rate_limit_returns_429(self):
        self.client.force_login(self.owner)
        with override_settings(DOWNLOAD_RATE_LIMIT_PER_MIN=1):
            self.assertEqual(self.client.get(self._url(self.doc)).status_code, 200)
            self.assertEqual(self.client.get(self._url(self.doc)).status_code, 429)
