# (c) cavaliba.com - tests / views_importer - CSV mode

# CELERY_TASK_ALWAYS_EAGER: every import is a DataTask now, so the write loop
# runs in the worker. Eager mode runs it inline instead, which keeps these tests
# worker-free and means the rows are written before the response comes back -
# the outcome is read from the task's output rather than from flash messages.

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.data import Instance
from app_data.models import DataTask
from tests import helper


@override_settings(CAVALIBA_AUTH_MODE="unittest", CELERY_TASK_ALWAYS_EAGER=True)
class ViewImportCsvTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_schema(
            classname="csvtest",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )

    def _post_csv(self, content, submit, extra=None):
        data = {
            "file": SimpleUploadedFile(
                "data.csv", content.encode("utf-8"), content_type="text/csv"
            ),
            "force_schema": "csvtest",
            "pipeline": "",
            "encoding": "utf-8",
            "separator": ",",
            "split_multivalue": "on",
            "submit": submit,
        }
        if extra:
            data.update(extra)
        return self.client.post(reverse("app_data:data_import"), data)

    def _output(self, response):
        """The submitted task's result dict."""

        return DataTask.objects.get(handle=response.context["task_handle"]).output

    def test_verify_reports_row_count(self):
        """ "verify" only reports the parsed row count - load_file_csv() never rejects
        a row itself (missing classname/keyname only fail once "import" reaches
        load_broker()), so there's nothing else to check at this stage. It is also
        the one submit that stays synchronous: no task, no handle."""

        response = self._post_csv("keyname,mystring\nrow1,hello\n", "verify")
        self.assertEqual(response.status_code, 200)
        rendered = [str(m) for m in response.context["messages"]]
        self.assertTrue(any("1 rows" in m for m in rendered))
        self.assertIsNone(response.context["task_handle"])

    def test_import_creates_instance(self):
        response = self._post_csv("keyname,mystring\nrow1,hello\n", "import")
        self.assertEqual(response.status_code, 200)
        instance = Instance.from_keyname(classname="csvtest", keyname="row1", expand=False)
        self.assertIsNotNone(instance)

    def test_import_opens_the_progress_modal(self):
        response = self._post_csv("keyname,mystring\nrow1,hello\n", "import")

        self.assertTrue(response.context["task_handle"])
        self.assertContains(response, "cv-import-modal")

    def test_import_ok_ko_counts(self):
        response = self._post_csv("keyname,mystring\nrow1,hello\nrow2,world\n", "import")

        output = self._output(response)
        self.assertEqual(output["count_ok"], 2)
        self.assertEqual(output["count_ko"], 0)
        self.assertFalse(output["aborted"])

    @override_settings(CAVALIBA_MAX_IMPORT_ERROR=2)
    def test_import_aborts_past_failure_threshold(self):
        """Every row targets a non-existent schema, so load_broker fails on each one;
        once failures exceed CAVALIBA_MAX_IMPORT_ERROR, the loop stops early.

        The form's "force_schema" field must be left empty here: it's forced onto
        every row unconditionally (same "always wins" semantics as --schema on
        the CLI/API), so a non-empty value here would override each row's own
        "doesnotexist" and defeat the test."""

        rows = "\n".join(f"row{i},doesnotexist" for i in range(1, 8))
        content = "keyname,_schema\n" + rows + "\n"
        response = self._post_csv(content, "import", extra={"force_schema": ""})

        output = self._output(response)
        self.assertTrue(output["aborted"])
        self.assertEqual(output["count_ok"], 0)

    @override_settings(CAVALIBA_MAX_IMPORT_ERROR=100)
    def test_every_failed_row_is_reported(self):
        """load_file_csv() never rejects a row - only load_broker() does (here: every
        row has no "_schema" column/value, so each is rejected for missing classname).
        The task output keeps one error per failed row, all 29 of them; capping is
        a display concern (the progress modal shows the first few). The failure
        threshold is raised so all rows are attempted rather than aborting early."""

        rows = "\n".join(f"row{i},hello" for i in range(1, 30))
        content = "keyname,mystring\n" + rows + "\n"
        response = self._post_csv(content, "import", extra={"force_schema": ""})

        output = self._output(response)
        self.assertEqual(output["count_ko"], 29)
        error_lines = [e for e in output["errors"] if "missing classname" in e]
        self.assertEqual(len(error_lines), 29)
