# (c) cavaliba.com - tests / views_importer - advanced section (force_action/force_schema/sync_mode)

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 tests import helper

# NOTE: CAVALIBA_AUTH_MODE="unittest" hardcodes the request's username to the
# literal string "unittest" (see app_data/aaa.py) - there is no way to switch
# identity per-request within a test method, so "admin" vs "non-admin" have to
# be two separate test classes, each creating "unittest" with a different
# permission profile in setUp().


def _post_import(client, content, submit, extra=None):
    """Post a CSV import to the unified Import Tool form."""

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


@override_settings(CAVALIBA_AUTH_MODE="unittest")
class ViewImporterAdvancedAdminTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_schema(classname="advtest")
        helper.add_schema(classname="advtest2")

    def test_admin_sees_advanced_section(self):
        response = self.client.get(reverse("app_data:data_import"))
        self.assertContains(response, "force_schema")
        self.assertContains(response, "force_action")

    def test_force_action_honored_for_admin(self):
        helper.add_instance(classname="advtest", keyname="row1")
        _post_import(
            self.client,
            "classname,keyname\nadvtest,row1\n",
            "import",
            extra={"force_action": "delete"},
        )
        instance = Instance.from_keyname(classname="advtest", keyname="row1", expand=False)
        self.assertIsNone(instance)

    def test_force_schema_honored_for_admin(self):
        _post_import(
            self.client,
            "classname,keyname,mystring\nadvtest,row2,hello\n",
            "import",
            extra={"force_schema": "advtest2"},
        )
        self.assertIsNone(Instance.from_keyname(classname="advtest", keyname="row2", expand=False))
        self.assertIsNotNone(
            Instance.from_keyname(classname="advtest2", keyname="row2", expand=False)
        )

    def test_sync_mode_off_by_default_leaves_last_sync_unset(self):
        _post_import(self.client, "classname,keyname\nadvtest,row3\n", "import")
        instance = Instance.from_keyname(classname="advtest", keyname="row3", expand=False)
        self.assertIsNone(instance.last_sync)

    def test_sync_mode_touches_last_sync_for_admin(self):
        _post_import(
            self.client, "classname,keyname\nadvtest,row4\n", "import", extra={"sync_mode": "on"}
        )
        instance = Instance.from_keyname(classname="advtest", keyname="row4", expand=False)
        self.assertIsNotNone(instance.last_sync)


@override_settings(CAVALIBA_AUTH_MODE="unittest")
class ViewImporterAdvancedNonAdminTest(TestCase):
    def setUp(self):
        cache.clear()
        # Full CRUD perms (but not p_data_admin) so that IF force_action/
        # force_schema were incorrectly honored, the row operation would
        # actually succeed - otherwise a denied-for-lack-of-CRUD-permission
        # row would make these assertions pass for the wrong reason.
        helper.add_user_with_perm(
            login="unittest",
            perms=["p_data_import", "p_data_create", "p_data_update", "p_data_delete"],
        )
        helper.add_schema(classname="advtest")
        helper.add_schema(classname="advtest2")

    def test_non_admin_does_not_see_advanced_section(self):
        response = self.client.get(reverse("app_data:data_import"))
        self.assertNotContains(response, "force_schema")
        self.assertNotContains(response, "force_action")

    def test_force_action_ignored_for_non_admin(self):
        """A crafted force_action=delete POSTed directly (bypassing the hidden
        UI section) must be silently dropped by ImportForm - the row has no
        _action column of its own, so the default "create" action just
        updates the existing instance rather than deleting it."""

        helper.add_instance(classname="advtest", keyname="row1")
        _post_import(
            self.client,
            "classname,keyname\nadvtest,row1\n",
            "import",
            extra={"force_action": "delete"},
        )
        instance = Instance.from_keyname(classname="advtest", keyname="row1", expand=False)
        self.assertIsNotNone(instance)

    def test_force_schema_ignored_for_non_admin(self):
        """A crafted force_schema=advtest2 POSTed directly must be silently
        dropped - the row's own classname ("advtest") wins."""

        _post_import(
            self.client,
            "classname,keyname,mystring\nadvtest,row2,hello\n",
            "import",
            extra={"force_schema": "advtest2"},
        )
        self.assertIsNotNone(
            Instance.from_keyname(classname="advtest", keyname="row2", expand=False)
        )
        self.assertIsNone(Instance.from_keyname(classname="advtest2", keyname="row2", expand=False))

    def test_sync_mode_ignored_for_non_admin(self):
        """A crafted sync_mode=on POSTed directly must be silently dropped -
        aaa["sync_mode"] stays False, so last_sync is never touched."""

        _post_import(
            self.client, "classname,keyname\nadvtest,row3\n", "import", extra={"sync_mode": "on"}
        )
        instance = Instance.from_keyname(classname="advtest", keyname="row3", expand=False)
        self.assertIsNone(instance.last_sync)
