# (c) cavaliba.com - tests / data / exporter view - column selector

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

import app_home.cache as cache
from tests import helper


class ViewExporterColumnSelectorTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")

        helper.add_schema(
            classname="selref",
            field_definition={
                "refstring": {"dataformat": "string", "displayname": "RefString"},
            },
        )

        helper.add_schema(
            classname="sel",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString", "order": 1},
                "myref": {
                    "dataformat": "schema",
                    "displayname": "MyRef",
                    "dataformat_ext": "selref refstring",
                    "order": 2,
                },
            },
        )
        helper.add_instance(classname="sel", keyname="sel-01", fields={"mystring": "one"})

        helper.add_instance(
            classname="_dataview",
            keyname="sel_small",
            fields={
                "target_class": "sel",
                "content": "columns:\n  - keyname\n  - MyString:\n      from: mystring\n",
            },
        )

    # ---------------------------------------------------------------
    # from the sidebar: an empty form
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_empty_form_has_no_schema_no_query_no_columns(self):
        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertEqual(response.context["schema_name"], "")
        self.assertEqual(response.context["values"]["query"], "")
        self.assertEqual(response.context["catalog"], [])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_empty_form_asks_for_a_schema_first(self):
        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertContains(response, "cv-export-columns")
        self.assertNotContains(response, 'name="columns"')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_no_dataview_selector_is_rendered(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertNotContains(response, 'name="dv"')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_no_exported_files_button(self):
        # the Download page keeps its own sidebar entry; only the in-page button is gone
        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertNotContains(response, "Exported files")

    # ---------------------------------------------------------------
    # toggle-all button and field name / displayname rendering
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_one_button_carries_both_toggle_labels(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertContains(response, 'id="cv-export-toggleall"')
        self.assertContains(response, 'data-label-all="Select all"')
        self.assertContains(response, 'data-label-none="Select none"')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_no_toggle_button_without_a_schema(self):
        # the page script still references the id - the button itself is what is absent
        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertNotContains(response, 'id="cv-export-toggleall"')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_field_name_is_separated_from_its_displayname(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertContains(response, '<span class="cv-export-col-name">mystring</span>')
        self.assertContains(response, '<span class="cv-export-col-sep">&ndash;</span>')
        self.assertContains(response, '<span class="cv-export-col-label">MyString</span>')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_no_separator_when_the_label_is_the_name(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        # "keyname" has no displayname of its own, so it gets no dash either
        self.assertNotContains(response, 'keyname</span><span class="cv-export-col-sep">')

    # ---------------------------------------------------------------
    # default checked set
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_mandatory_columns_always_checked(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertIn("_schema", response.context["checked"])
        self.assertIn("keyname", response.context["checked"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_mandatory_columns_are_posted_by_a_hidden_companion(self):
        # a disabled checkbox is never submitted, so the hidden input is what
        # actually carries the value
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertContains(response, '<input type="hidden" name="columns" value="_schema">')
        self.assertContains(response, '<input type="hidden" name="columns" value="keyname">')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_builtins_and_schema_fields_checked_by_default(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        checked = response.context["checked"]
        for name in ("displayname", "is_enabled", "last_sync", "last_update", "mystring", "myref"):
            self.assertIn(name, checked)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_injected_columns_unchecked_by_default(self):
        response = self.client.get(reverse("app_data:data_exporter"), {"schema": "sel"})

        self.assertIn("myref__refstring", [e["name"] for e in response.context["catalog"]])
        self.assertNotIn("myref__refstring", response.context["checked"])

    # ---------------------------------------------------------------
    # from a list page: ?dv= pre-checks the view's columns
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_dataview_param_prechecks_its_own_columns(self):
        response = self.client.get(
            reverse("app_data:data_exporter"), {"schema": "sel", "dv": "sel_small"}
        )

        checked = response.context["checked"]
        self.assertIn("mystring", checked)
        self.assertIn("keyname", checked)
        self.assertNotIn("myref", checked)
        self.assertNotIn("last_update", checked)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_dataview_param_still_checks_the_mandatory_columns(self):
        response = self.client.get(
            reverse("app_data:data_exporter"), {"schema": "sel", "dv": "sel_small"}
        )

        self.assertIn("_schema", response.context["checked"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_unknown_dataview_falls_back_to_the_default_checked_set(self):
        response = self.client.get(
            reverse("app_data:data_exporter"), {"schema": "sel", "dv": "nosuchview"}
        )

        self.assertIn("myref", response.context["checked"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_get_does_not_touch_the_session_dataview(self):
        self.client.get(reverse("app_data:data_exporter"), {"schema": "sel", "dv": "sel_small"})

        self.assertNotIn("dataviews", self.client.session)

    # ---------------------------------------------------------------
    # htmx partial on a schema change
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_partial_rebuilds_the_selector_for_the_new_schema(self):
        response = self.client.get(reverse("app_data:data_exporter_fields"), {"schema": "sel"})

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "cv-export-columns")
        self.assertContains(response, "mystring")
        self.assertIn("mystring", response.context["checked"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_partial_without_a_schema_offers_nothing(self):
        response = self.client.get(reverse("app_data:data_exporter_fields"), {"schema": ""})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context["catalog"], [])
        self.assertNotContains(response, 'name="columns"')

    # ---------------------------------------------------------------
    # POST - the selected columns drive the export
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_checked_boxes_become_the_exported_columns_in_order(self):
        response = self.client.post(
            reverse("app_data:data_exporter"),
            {
                "schema": "sel",
                "format": "csv",
                "columns": ["_schema", "keyname", "myref__refstring", "mystring"],
            },
        )

        lines = response.content.decode().splitlines()
        self.assertEqual(lines[0], "_schema,keyname,myref__refstring,mystring")
