# (c) cavaliba.com - tests / data / exporter view - result download gating

import shutil
import tempfile
import uuid

from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import timezone

import app_home.cache as cache
from app_data.loader import load_broker
from app_data.models import DataFile, DataTask
from app_home.configuration import set_configuration
from app_user.models import SirenePermission
from tests import helper

BASE_PERMS = ["p_data_export", "p_data_access", "p_data_read"]


@override_settings(CAVALIBA_AUTH_MODE="unittest", CELERY_TASK_ALWAYS_EAGER=True)
class ViewExporterResultTest(TestCase):
    def _isolate_filestore(self):
        """Point the filestore at a throwaway directory for this test.

        An async export writes a real blob, and the test DB is dropped at the end
        of the run while the blob is not - so without this every run would leave
        orphans behind in the shared CAVALIBA_FILESTORE.
        """

        rootdir = tempfile.mkdtemp(prefix="cavaliba_test_filestore_")
        override = override_settings(CAVALIBA_FILESTORE=rootdir)
        override.enable()
        self.addCleanup(override.disable)
        self.addCleanup(shutil.rmtree, rootdir, ignore_errors=True)
        return rootdir

    def setUp(self):
        self._isolate_filestore()
        cache.clear()
        set_configuration(
            appname="data", keyname="EXPORT_INTERACTIVE_MAX_SIZE",
            value=2, description="", page="", order=100,
        )  # fmt: skip
        cache.clear()

        # a plain user, not an admin: an admin carries p_task_all, which would
        # mask every ownership check below
        helper.add_user_with_perm(login="unittest", perms=BASE_PERMS)
        helper.add_schema(
            classname="vres",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        for i in range(1, 6):
            helper.add_instance(
                classname="vres", keyname=f"vres-{i:02d}", fields={"mystring": f"row {i}"}
            )

        self.client.post(
            reverse("app_data:data_exporter"),
            {"schema": "vres", "format": "csv", "columns": "*"},
        )
        self.task = DataTask.objects.first()

    def _set_perms(self, perms):
        for perm_name in perms:
            SirenePermission.objects.get_or_create(keyname=perm_name)
        load_broker(
            datalist=[
                {
                    "classname": "role",
                    "_action": "update",
                    "keyname": "role_unittest",
                    "permissions": perms,
                }
            ],
            aaa={"perms": ["p_role_update"]},
        )
        cache.clear()
        self.client.session.flush()

    def _url(self, handle=None):
        return reverse("app_data:data_exporter_result", args=[handle or self.task.handle])

    def test_task_was_created_for_this_user(self):
        self.assertEqual(self.task.owner_id, "unittest")
        self.assertEqual(self.task.state, "DONE")

    def test_owner_can_download_the_result(self):
        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 200)
        body = b"".join(response.streaming_content).decode()
        self.assertEqual(len(body.splitlines()), 6)
        self.assertIn("vres-01", body)

    def test_filename_is_offered_for_download(self):
        response = self.client.get(self._url())

        # as_attachment, not inline: a browser must save the file, not open it
        self.assertIn("attachment;", response["Content-Disposition"])
        self.assertIn("vres-", response["Content-Disposition"])
        self.assertIn(".csv", response["Content-Disposition"])

    def test_unknown_handle_is_404(self):
        response = self.client.get(self._url(handle=uuid.uuid4()))

        self.assertEqual(response.status_code, 404)

    def test_another_users_task_is_404(self):
        self.task.owner_id = "someoneelse"
        self.task.save(update_fields=["owner_id"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 404)

    def test_p_task_all_can_download_another_users_result(self):
        self.task.owner_id = "someoneelse"
        self.task.save(update_fields=["owner_id"])
        self._set_perms([*BASE_PERMS, "p_task_all"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 200)

    def test_running_task_has_no_downloadable_result(self):
        self.task.state = "RUNNING"
        self.task.save(update_fields=["state"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 302)

    def test_task_without_attachment_refused(self):
        self.task.attachment = None
        self.task.save(update_fields=["attachment"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 302)

    def test_expired_file_refused(self):
        datafile = DataFile.objects.get(fileid=self.task.attachment["fileid"])
        datafile.not_after = timezone.now() - timezone.timedelta(hours=1)
        datafile.save(update_fields=["not_after"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 302)

    def test_reaped_datafile_refused(self):
        DataFile.objects.filter(fileid=self.task.attachment["fileid"]).delete()

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 302)

    def test_without_p_data_export_redirected(self):
        self._set_perms(["p_data_access", "p_data_read"])

        response = self.client.get(self._url())

        self.assertEqual(response.status_code, 302)
