# (c) cavaliba.com - tests data export

import hashlib
import json
import os

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

import app_home.cache as cache
from app_data.revision import revision_add_raw
from tests import helper


class DataExportTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_schema(
            classname="test1",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        helper.add_instance(
            classname="test1",
            keyname="test1-01",
            fields={
                "mystring": "data for test1-01",
            },
        )
        helper.add_instance(
            classname="_dataview",
            keyname="test1_enumerate_inject",
            fields={
                "target_class": "test1",
                "content": "columns:\n  - keyname\n  - Widget:\n      from: my_enumerate_subfields__widget\n",
            },
        )
        helper.add_instance(
            classname="_dataview",
            keyname="test1_welcome",
            fields={
                "target_class": "test1",
                "content": "columns:\n  - keyname\n  - invalid:\n      from: non_existent_column\n",
            },
        )

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_csv(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "___ALL___",
                "query": "",
                "format": "csv",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_yaml(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "___ALL___",
                "query": "",
                "format": "yaml",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_json(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "___ALL___",
                "query": "",
                "format": "json",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_yaml_dataview1(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "test1_enumerate_inject",
                "query": "",
                "format": "yaml",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")
        self.assertContains(response, "Widget")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_yaml_dataview2(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "test1_welcome",
                "query": "",
                "format": "yaml",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")
        self.assertContains(response, "invalid")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_export_json_dataview(self):
        response = self.client.post(
            reverse("app_data:data_export"),
            {
                "classname": "test1",
                "page": "allpages",
                "dv": "test1_welcome",
                "query": "",
                "format": "json",
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

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

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Export Viewer")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_viewer_file_list_and_view(self):
        export_folder = str(settings.CAVALIBA_EXPORT_FOLDER)
        os.makedirs(export_folder, exist_ok=True)
        test_file = os.path.join(export_folder, "unittest.txt")
        test_content = "This is a test export file\n"
        with open(test_file, "w") as f:
            f.write(test_content)

        try:
            response = self.client.get(reverse("app_data:data_export"))
            self.assertEqual(response.status_code, 200)
            self.assertContains(response, "unittest.txt")

            filename_md5 = hashlib.md5(b"unittest.txt").hexdigest()

            download_response = self.client.get(
                reverse("app_data:data_export") + f"?dl={filename_md5}"
            )
            self.assertEqual(download_response.status_code, 200)
            self.assertIn("Content-Disposition", download_response)
            self.assertIn("attachment", download_response["Content-Disposition"])

            view_response = self.client.get(
                reverse("app_data:data_export") + f"?view={filename_md5}"
            )
            self.assertEqual(view_response.status_code, 200)
            self.assertContains(view_response, test_content.strip())
            self.assertContains(view_response, "unittest.txt")

        finally:
            if os.path.exists(test_file):
                os.remove(test_file)


class DataExportRefsRevTest(TestCase):
    def setUp(self):
        cache.clear()
        helper.add_admin_user(login="unittest")
        helper.add_user(login="testuser01")
        helper.add_schema(
            classname="test0",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
            },
        )
        helper.add_instance(
            classname="test0",
            keyname="test0-01",
            fields={
                "mystring": "data for test0-01",
            },
        )
        helper.add_schema(
            classname="test1",
            field_definition={
                "mystring": {"dataformat": "string", "displayname": "MyString"},
                "my_user": {"dataformat": "user", "displayname": "My User"},
                "my_schema_test0": {
                    "dataformat": "schema",
                    "dataformat_ext": "test0",
                    "displayname": "Schema Test0",
                },
            },
        )
        helper.add_instance(
            classname="test1",
            keyname="test1-01",
            fields={
                "mystring": "data for test1-01",
                "my_user": "testuser01",
                "my_schema_test0": "test0-01",
            },
        )

    def _post(self, extra=None):
        data = {
            "classname": "test1",
            "page": "allpages",
            "dv": "___ALL___",
            "query": "",
            "format": "yaml",
        }
        if extra:
            data.update(extra)
        return self.client.post(reverse("app_data:data_export"), data)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_refs_user(self):
        response = self._post({"refs": "user"})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertIn("_refs", content)
        self.assertIn("testuser01", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_json_refs_user(self):
        response = self._post({"format": "json", "refs": "user"})
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        refs_found = any("_refs" in item for item in data)
        self.assertTrue(refs_found)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_refs_schema(self):
        response = self._post({"refs": "test0"})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertIn("_refs", content)
        self.assertIn("test0", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_refs_empty_no_refs_key(self):
        response = self._post({"refs": ""})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertNotIn("_refs", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_refs_unknown_no_refs_key(self):
        response = self._post({"refs": "nonexistent_schema"})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertNotIn("_refs", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_csv_refs_ignored(self):
        response = self._post({"format": "csv", "refs": "user"})
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_rev_with_revisions(self):
        revision_add_raw(aaa=None, classname="test1", keyname="test1-01", action="edit")
        response = self._post({"rev": "3"})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertIn("_revision", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_json_rev_with_revisions(self):
        revision_add_raw(aaa=None, classname="test1", keyname="test1-01", action="edit")
        response = self._post({"format": "json", "rev": "1"})
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        rev_found = any("_revision" in item for item in data)
        self.assertTrue(rev_found)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_rev_no_revisions(self):
        response = self._post({"rev": "5"})
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()
        self.assertNotIn("_revision", content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_rev_invalid_value(self):
        response = self._post({"rev": "notanumber"})
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_csv_rev_ignored(self):
        revision_add_raw(aaa=None, classname="test1", keyname="test1-01", action="edit")
        response = self._post({"format": "csv", "rev": "1"})
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "test1-01")
