# (c) cavaliba.com - tests / data / exporter view - synchronous download path

import json

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

import app_home.cache as cache
from tests import helper


class ViewExporterSyncTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_schema(
            classname="vexp",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        helper.add_instance(classname="vexp", keyname="vexp-01", fields={"mystring": "one"})
        helper.add_instance(classname="vexp", keyname="vexp-02", fields={"mystring": "two"})

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

    def _post(self, **extra):
        data = {"schema": "vexp", "format": "csv", "columns": "*"}
        data.update(extra)
        return self.client.post(reverse("app_data:data_exporter"), data)

    # ---------------------------------------------------------------
    # GET - form + prepopulation
    # ---------------------------------------------------------------

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

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context["schema_name"], "")
        self.assertEqual(response.context["catalog"], [])

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

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context["schema_name"], "vexp")
        self.assertIn("mystring", [e["name"] for e in response.context["catalog"]])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_query_param_prepopulates(self):
        response = self.client.get(
            reverse("app_data:data_exporter"), {"schema": "vexp", "query": "mystring:one"}
        )

        self.assertEqual(response.context["values"]["query"], "mystring:one")

    @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": "vexp", "dv": "vexp_small"})

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

    # ---------------------------------------------------------------
    # POST - small export downloads inline
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_csv_download(self):
        response = self._post()

        self.assertEqual(response.status_code, 200)
        self.assertIn("text/csv", response["Content-Type"])
        self.assertIn("attachment;", response["Content-Disposition"])
        self.assertIn("vexp-", response["Content-Disposition"])
        lines = response.content.decode().splitlines()
        self.assertTrue(lines[0].startswith("_schema,keyname"))
        self.assertEqual(len(lines), 3)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_yaml_download(self):
        response = self._post(format="yaml")

        self.assertEqual(response.status_code, 200)
        data = yaml.safe_load(response.content.decode())
        self.assertEqual(len(data), 2)
        self.assertEqual(data[0]["classname"], "vexp")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_json_download(self):
        response = self._post(format="json")

        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content.decode())
        self.assertEqual(len(data), 2)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_query_narrows_the_export(self):
        response = self._post(query="mystring:one")

        lines = response.content.decode().splitlines()
        self.assertEqual(len(lines), 2)
        self.assertIn("vexp-01", lines[1])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_explicit_columns_respected(self):
        response = self._post(columns="keyname,mystring")

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

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_schema_column_is_always_emitted(self):
        response = self._post(columns="keyname,mystring")

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

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_action_column_added_first(self):
        response = self._post(action="noop")

        lines = response.content.decode().splitlines()
        self.assertTrue(lines[0].startswith("_action,_schema,keyname"))
        self.assertTrue(lines[1].startswith("noop,vexp,"))

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_custom_delimiter(self):
        response = self._post(columns="keyname", delimiter=";")

        self.assertEqual(response.content.decode().splitlines()[0], "_schema;keyname")

    # ---------------------------------------------------------------
    # POST - rejected input stays on the form and keeps the values
    # ---------------------------------------------------------------

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_bad_format_rerenders_the_form(self):
        response = self._post(format="xlsx")

        self.assertEqual(response.status_code, 200)
        self.assertIn("text/html", response["Content-Type"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_rejected_input_keeps_the_checked_columns(self):
        response = self._post(format="xlsx", columns="keyname,mystring")

        self.assertIn("mystring", response.context["checked"])
        self.assertNotIn("displayname", response.context["checked"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_unknown_schema_rerenders_the_form(self):
        response = self._post(schema="nosuchschema")

        self.assertEqual(response.status_code, 200)
        self.assertIn("text/html", response["Content-Type"])

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_successful_post_does_not_create_a_task(self):
        from app_data.models import DataTask

        self._post()

        self.assertEqual(DataTask.objects.count(), 0)


class ViewExporterPermissionTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_schema(
            classname="vexpperm",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        helper.add_instance(classname="vexpperm", keyname="vexpperm-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_without_p_data_export_redirected(self):
        helper.add_user_with_perm(login="unittest", perms=["p_data_access"])

        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertEqual(response.status_code, 302)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_with_p_data_export_allowed(self):
        helper.add_user_with_perm(login="unittest", perms=["p_data_export"])

        response = self.client.get(reverse("app_data:data_exporter"))

        self.assertEqual(response.status_code, 200)
