# (c) cavaliba.com - tests data export

import hashlib
import json
import os

import app_home.cache as cache
from app_data.revision import revision_add_raw
from app_user.models import SireneUser
from app_user.role import role_get_by_name
from django.conf import settings
from django.test import TestCase, override_settings
from django.urls import reverse


class DataExportTest(TestCase):

    fixtures = ["test"]

    def setUp(self):
        user = SireneUser.objects.create(
            login="unittest", firstname="unittestname")
        user.save()
        role = role_get_by_name("role_admin")
        role.users.add(user)
        role.save()
        cache.clear()

    @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")
        # print(response.content)

    @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")
        # print(response.content)

    @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")
        # print(response.content)

    # with dataview

    @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")
        # print(response.content)

    @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',
        })

        # print(response.content)
        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")
        # print(response.content)

    @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):
        # Step 1: Create a test file in the export folder
        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:
            # Step 2: GET the export viewer to list files
            response = self.client.get(reverse('app_data:data_export'))
            self.assertEqual(response.status_code, 200)

            # Step 3: Check for the file entry in the table
            self.assertContains(response, 'unittest.txt')

            # Calculate the expected MD5 hash
            filename_md5 = hashlib.md5(b'unittest.txt').hexdigest()

            # Step 4: Follow the download URL and check for FileResponse
            download_response = self.client.get(
                reverse('app_data:data_export') + f'?dl={filename_md5}'
            )
            self.assertEqual(download_response.status_code, 200)
            # Check that it's a file response with proper headers
            self.assertIn('Content-Disposition', download_response)
            self.assertIn('attachment', download_response['Content-Disposition'])

            # Step 5: Follow the view URL and check for file content
            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')  # filename in title

        finally:
            # Cleanup: remove the test file
            if os.path.exists(test_file):
                os.remove(test_file)


# ---------------------------------------------------------------------------
# Tests for refs and rev POST parameters on the UI export view
# Uses fixture "test" which contains test1 instances with my_user/my_schema_test0
# fields referencing testuser01 and test0-01.
# ---------------------------------------------------------------------------

class DataExportRefsRevTest(TestCase):

    fixtures = ["test"]

    def setUp(self):
        user = SireneUser.objects.create(login="unittest", firstname="unittestname")
        user.save()
        role = role_get_by_name("role_admin")
        role.users.add(user)
        role.save()
        cache.clear()

    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)

    # --- refs ---

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_export_yaml_refs_user(self):
        # test1-01 has my_user=testuser01 ; refs=_user should inline _refs._user
        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):
        # test1-01 has my_schema_test0=test0-01 ; refs=test0 should inline _refs.test0
        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):
        # CSV format does not use refs; should succeed without error
        response = self._post({'format': 'csv', 'refs': '_user'})
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'test1-01')

    # --- rev ---

    @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):
        # rev requested but no revisions exist; should succeed without _revision key
        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):
        # invalid rev should be silently ignored
        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):
        # CSV format does not use rev; should succeed without error
        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')
