# (c) cavaliba.com - tests / data / exporter progress modal partial

import shutil
import tempfile

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

import app_home.cache as cache
from app_data.exporter import ExportSpec
from app_data.models import DataTask
from app_data.task_manager import create_datatask
from app_data.tasks import task_export
from tests import helper

AAA = {"username": "unittest", "perms": ["p_data_admin"]}


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

        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()
        helper.add_admin_user(login="unittest")
        helper.add_schema(
            classname="vprog",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        # deliberately fewer rows than exporter.BATCH_SIZE, so the task never
        # calls on_batch and never writes a `progress` dict at all
        for i in range(1, 6):
            helper.add_instance(
                classname="vprog", keyname=f"vprog-{i:02d}", fields={"mystring": f"row {i}"}
            )

        self.params = {"spec": ExportSpec(schema="vprog", format="yaml").to_dict(), "aaa": AAA}
        self.task = create_datatask(
            name="Export yaml - vprog", params=self.params, owner_type="user", owner_id="unittest"
        )

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

    def _run(self):
        task_export(str(self.task.handle), self.params)
        self.task.refresh_from_db()

    # ---------------------------------------------------------------
    # the title follows the task state (it lives inside the partial)
    # ---------------------------------------------------------------

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

        self.assertContains(response, "Export queued")
        self.assertNotContains(response, "Export done")

    def test_done_task_does_not_say_in_progress(self):
        self._run()

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

        self.assertEqual(self.task.state, "DONE")
        self.assertContains(response, "Export done")
        self.assertNotContains(response, "Export in progress")

    def test_aborted_task_says_aborted(self):
        DataTask.objects.filter(handle=self.task.handle).update(state="ABORTED")

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

        self.assertContains(response, "Export aborted")
        self.assertNotContains(response, "Export in progress")

    def test_failed_task_says_failed(self):
        DataTask.objects.filter(handle=self.task.handle).update(state="FAILED")

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

        self.assertContains(response, "Export failed")
        self.assertNotContains(response, "Export in progress")

    # ---------------------------------------------------------------
    # figures survive a task that never reported progress
    # ---------------------------------------------------------------

    def test_sub_batch_export_reports_full_percent(self):
        self._run()

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

        self.assertIsNone(self.task.progress.get("count"))
        self.assertEqual(response.context["percent"], 100)
        self.assertEqual(response.context["percent_step"], 100)

    def test_sub_batch_export_reports_counts(self):
        self._run()

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

        self.assertEqual(response.context["total"], 5)
        self.assertEqual(response.context["count_done"], 5)
        self.assertEqual(response.context["count_ok"], 5)
        self.assertEqual(response.context["count_ko"], 0)

    def test_sub_batch_export_reports_a_rate(self):
        self._run()

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

        self.assertTrue(self.task.duration)
        self.assertIsNotNone(response.context["rate"])
        self.assertGreater(response.context["rate"], 0)

    def test_finished_task_has_no_remaining_row(self):
        self._run()

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

        self.assertIsNone(response.context["eta"])
        self.assertNotContains(response, "Remaining")

    # ---------------------------------------------------------------
    # polling and buttons
    # ---------------------------------------------------------------

    def test_running_task_keeps_polling(self):
        DataTask.objects.filter(handle=self.task.handle).update(state="RUNNING")

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

        self.assertContains(response, "every 2s")
        self.assertContains(response, "Abort")

    def test_finished_task_stops_polling(self):
        self._run()

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

        self.assertNotContains(response, "every 2s")
        self.assertNotContains(response, "Abort")

    def test_finished_task_offers_the_result_link(self):
        self._run()

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

        self.assertContains(
            response, reverse("app_data:data_exporter_result", args=[self.task.handle])
        )  # noqa: E501
        self.assertContains(response, ".yaml")

    def test_partial_carries_real_modal_blocks(self):
        # the padding bug: the figures must sit in a real .modal-body, and the
        # header/footer must be their own blocks, or Bootstrap applies no padding
        self._run()

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

        self.assertContains(response, 'class="modal-header"')
        self.assertContains(response, 'class="modal-body"')
        self.assertContains(response, 'class="modal-footer"')
