"""Trashing documents and groups — reversible by design."""
import shutil
import tempfile

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

from documents.models import Category, Collection, Document

User = get_user_model()
_TMP = tempfile.mkdtemp(prefix="hartling-trash-test-")


def pdf(name="a.pdf"):
    return SimpleUploadedFile(name, b"%PDF-1.4", content_type="application/pdf")


@override_settings(PRIVATE_MEDIA_ROOT=_TMP)
class TrashTests(TestCase):
    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(_TMP, ignore_errors=True)
        super().tearDownClass()

    def setUp(self):
        self.staff = User.objects.create_user("staff", password="pw-staff-12345", is_staff=True)
        self.owner = User.objects.create_user("owner", password="pw-owner-12345")
        self.category = Category.objects.create(name="Minutes", slug="minutes")
        self.group = Collection.objects.create(
            title="2026 AGM documents", slug="agm-2026", occurred_on="2026-05-13")
        self.inside = Document.objects.create(
            title="AGM Minutes", category=self.category, collection=self.group,
            published=True, published_date="2026-05-13", file=pdf())
        self.also_inside = Document.objects.create(
            title="Financial Statements", category=self.category, collection=self.group,
            published=True, published_date="2026-05-13", file=pdf("b.pdf"))
        self.loose = Document.objects.create(
            title="Standalone paper", category=self.category,
            published=True, published_date="2026-01-01", file=pdf("c.pdf"))

    # ---- permissions ----
    def test_owners_cannot_trash_a_document(self):
        self.client.force_login(self.owner)
        response = self.client.post(
            reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.assertEqual(response.status_code, 302)
        self.loose.refresh_from_db()
        self.assertIsNone(self.loose.removed_at)

    def test_owners_cannot_trash_a_group(self):
        self.client.force_login(self.owner)
        self.client.post(reverse("backoffice:collection_trash", args=[self.group.pk]), secure=True)
        self.group.refresh_from_db()
        self.assertIsNone(self.group.removed_at)

    def test_owners_never_see_a_trash_control(self):
        """Regression: the trash buttons were briefly outside the staff block.

        Matches the RENDERED urls — asserting on the template tag name proves
        nothing, because {% url %} never emits it.
        """
        import re

        doc_trash = re.compile(r"/staff/documents/\d+/trash/")
        group_trash = re.compile(r"/staff/groups/\d+/trash/")

        self.client.force_login(self.staff)
        staff_body = self.client.get(reverse("documents:library"), secure=True).content.decode()
        self.assertTrue(doc_trash.search(staff_body), "staff should get a trash control")
        self.assertTrue(group_trash.search(staff_body))

        owner_client = Client()
        owner_client.force_login(self.owner)
        owner_body = owner_client.get(reverse("documents:library"), secure=True).content.decode()
        self.assertIsNone(doc_trash.search(owner_body))
        self.assertIsNone(group_trash.search(owner_body))
        self.assertNotIn("Move to trash", owner_body)

    def test_owners_cannot_reach_the_trash_page(self):
        self.client.force_login(self.owner)
        self.assertEqual(
            self.client.get(reverse("backoffice:trash"), secure=True).status_code, 302)

    def test_trashing_requires_post(self):
        self.client.force_login(self.staff)
        response = self.client.get(
            reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.assertEqual(response.status_code, 405)

    # ---- documents ----
    def test_trash_a_document_and_it_leaves_the_library(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.loose.refresh_from_db()
        self.assertIsNotNone(self.loose.removed_at)

        owner_client = Client()
        owner_client.force_login(self.owner)
        body = owner_client.get(reverse("documents:library"), secure=True).content.decode()
        self.assertNotIn("Standalone paper", body)

    def test_a_trashed_document_keeps_its_file(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.loose.refresh_from_db()
        self.assertEqual(self.loose.file.read(), b"%PDF-1.4")

    def test_owners_cannot_download_a_trashed_document(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        owner_client = Client()
        owner_client.force_login(self.owner)
        response = owner_client.get(
            reverse("documents:download", args=[self.loose.pk]), secure=True)
        self.assertEqual(response.status_code, 404)

    def test_restore_a_document(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.client.post(reverse("backoffice:document_restore", args=[self.loose.pk]), secure=True)
        self.loose.refresh_from_db()
        self.assertIsNone(self.loose.removed_at)

    # ---- groups ----
    def test_trashing_a_group_takes_its_documents(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:collection_trash", args=[self.group.pk]), secure=True)
        self.group.refresh_from_db()
        self.inside.refresh_from_db()
        self.also_inside.refresh_from_db()
        self.assertIsNotNone(self.group.removed_at)
        self.assertIsNotNone(self.inside.removed_at)
        self.assertIsNotNone(self.also_inside.removed_at)
        # ...and leaves everything else alone.
        self.loose.refresh_from_db()
        self.assertIsNone(self.loose.removed_at)

    def test_restoring_a_group_brings_its_documents_back(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:collection_trash", args=[self.group.pk]), secure=True)
        self.client.post(reverse("backoffice:collection_restore", args=[self.group.pk]), secure=True)
        self.group.refresh_from_db(); self.inside.refresh_from_db()
        self.assertIsNone(self.group.removed_at)
        self.assertIsNone(self.inside.removed_at)

    def test_a_document_already_in_the_trash_is_not_swept_back_by_a_group_restore(self):
        """It was trashed for its own reasons before the group was."""
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.inside.pk]), secure=True)
        self.client.post(reverse("backoffice:collection_trash", args=[self.group.pk]), secure=True)
        self.client.post(reverse("backoffice:collection_restore", args=[self.group.pk]), secure=True)
        self.inside.refresh_from_db(); self.also_inside.refresh_from_db()
        self.assertIsNotNone(self.inside.removed_at)   # stays in the trash
        self.assertIsNone(self.also_inside.removed_at)  # came back with the group

    # ---- the trash page ----
    def test_the_trash_page_lists_what_is_in_it(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        response = self.client.get(reverse("backoffice:trash"), secure=True)
        self.assertContains(response, "Standalone paper")

    def test_emptying_the_trash_deletes_for_good(self):
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:document_trash", args=[self.loose.pk]), secure=True)
        self.client.post(reverse("backoffice:trash_empty"), secure=True)
        self.assertFalse(Document.objects.filter(pk=self.loose.pk).exists())
        # Untouched documents survive.
        self.assertTrue(Document.objects.filter(pk=self.inside.pk).exists())
