# (c) cavaliba.com - tests / views_importer - large file async DataTask path

import os
import tempfile
from unittest.mock import patch

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

import app_home.cache as cache
from app_data.models import DataTask
from app_data.task_manager import create_datatask
from app_data.tasks import task_import
from tests import helper


def _csv_rows(classname, count):
    rows = "\n".join(f"{classname},row{i},v{i}" for i in range(1, count + 1))
    return f"classname,keyname,mystring\n{rows}\n"


@override_settings(CAVALIBA_AUTH_MODE="unittest", CAVALIBA_MAX_SYNC_IMPORT=2)
class ViewImporterAsyncRefusedTest(TestCase):
    """A user with p_data_import but not p_task_view can't be redirected to a
    progress page they're not allowed to see - so a large import is refused
    upfront rather than silently submitted."""

    def setUp(self):
        cache.clear()
        helper.add_user_with_perm(login="unittest", perms=["p_data_import"])
        helper.add_schema(classname="asyncrefused")

    def test_large_import_is_refused_without_p_task_view(self):
        content = _csv_rows("asyncrefused", 3)
        data = {
            "file": SimpleUploadedFile(
                "data.csv", content.encode("utf-8"), content_type="text/csv"
            ),
            "pipeline": "",
            "encoding": "utf-8",
            "separator": ",",
            "split_multivalue": "on",
            "submit": "import",
        }
        response = self.client.post(reverse("app_data:data_import"), data)

        self.assertEqual(response.status_code, 200)
        rendered = [str(m) for m in response.context["messages"]]
        self.assertTrue(any("too large" in m for m in rendered))
        self.assertEqual(DataTask.objects.count(), 0)


@override_settings(
    CAVALIBA_AUTH_MODE="unittest", CAVALIBA_MAX_SYNC_IMPORT=2, CELERY_TASK_ALWAYS_EAGER=True
)
class ViewImporterAsyncSubmittedTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_user_with_perm(login="unittest", perms=["p_data_import", "p_task_view"])
        helper.add_schema(
            classname="asyncsubmitted",
            field_definition={"mystring": {"dataformat": "string", "displayname": "MyString"}},
        )

    def test_large_import_submits_datatask_for_permitted_user(self):
        content = _csv_rows("asyncsubmitted", 3)
        data = {
            "file": SimpleUploadedFile(
                "data.csv", content.encode("utf-8"), content_type="text/csv"
            ),
            "pipeline": "",
            "encoding": "utf-8",
            "separator": ",",
            "split_multivalue": "on",
            "submit": "import",
        }
        response = self.client.post(reverse("app_data:data_import"), data)

        self.assertEqual(response.status_code, 302)
        self.assertIn("/private/tasks/", response.url)

        dt = DataTask.objects.filter(name__startswith="Import").latest("created_at")
        self.assertIn(dt.state, ("QUEUED", "RUNNING", "DONE"))


class TaskImportTest(TestCase):
    """task_import() called directly, mirroring test_pipeline_submit.py's
    TaskPipelineAbortTest pattern - no Celery worker/Redis needed."""

    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_schema(
            classname="taskimporttest",
            field_definition={"mystring": {"dataformat": "string", "displayname": "MyString"}},
        )

    def _write_csv(self, content):
        fd, path = tempfile.mkstemp(suffix=".csv")
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            f.write(content)
        return path

    def _params(self, path):
        return {
            "filename": path,
            "file_kind": "csv",
            "parse_options": {
                "encoding": "utf-8",
                "csv_delimiter": ",",
                "no_multi": False,
                "first": 1,
                "last": 0,
            },
            "aaa": {"perms": ["p_data_admin"], "username": "unittest"},
            "pipeline_name": None,
            "force_action": None,
            "force_schema": None,
        }

    def test_task_import_runs_end_to_end(self):
        path = self._write_csv(_csv_rows("taskimporttest", 5))
        try:
            params = self._params(path)
            dt = create_datatask("import test", params=params)
            task_import(str(dt.handle), params)
            dt.refresh_from_db()
            self.assertEqual(dt.state, "DONE")
            self.assertEqual(dt.output["count_ok"], 5)
        finally:
            os.remove(path)

    def test_task_import_aborts_mid_run(self):
        path = self._write_csv(_csv_rows("taskimporttest", 25))
        try:
            params = self._params(path)
            dt = create_datatask("import abort test", params=params)

            abort_calls = {"count": 0}

            def fake_is_aborted(handle):
                abort_calls["count"] += 1
                return abort_calls["count"] >= 1

            with patch("app_data.tasks.is_aborted", side_effect=fake_is_aborted):
                task_import(str(dt.handle), params)
            dt.refresh_from_db()
            self.assertEqual(dt.state, "ABORTED")
        finally:
            os.remove(path)

    def test_task_import_reports_parse_error(self):
        params = self._params("/no/such/file.csv")
        dt = create_datatask("import parse error test", params=params)
        task_import(str(dt.handle), params)
        dt.refresh_from_db()
        self.assertEqual(dt.state, "FAILED")
        self.assertIn("error", dt.output)
