# (c) cavaliba.com - test / views

from django.test import override_settings
from django.test import TestCase
from django.test import TransactionTestCase
from django.urls import reverse
from django.core.cache import cache as cache_django

from tests import helper
import app_home.cache as cache


from app_user.models import SireneUser
from app_user.group import group_get_by_id
from app_user.role import role_get_by_id

# HOME
# public(index), private, admintools_xxxx
#

class TestViews(TestCase):

    fixtures = ["init"]

    def setUp(self):
        #helper.add_admin_user(login="unittest")
        cache.clear()
        cache_django.clear()


    # HOME / public (index)
    # ---------------------
    def test_view_home_index_noauth(self):

        #helper.add_user_with_perm(login="unittest", perms=[])
        response = self.client.get(reverse('app_home:index'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Please sign-in', status_code=200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_view_home_index_auth_default_role(self):

        helper.add_user_with_perm(login="unittest", perms=[])

        #helper.add_user_with_perm(login="unittest", perms=[])
        response = self.client.get(reverse('app_home:index'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Welcome', status_code=200)



    # HOME / private
    # --------------
    def test_view_home_private_noauth(self):

        response = self.client.get(reverse('app_home:private'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Please sign-in', status_code=200)


    # auth, no perm but default_role allow p_home_access
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_view_home_private_auth_noperm_but_default_role(self):

        helper.add_user_with_perm(login="unittest", perms=[])
        response = self.client.get(reverse('app_home:private'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Welcome', status_code=200)
        self.assertContains(response, 'Services', status_code=200)
        self.assertContains(response, 'Sirene', status_code=200)
        self.assertContains(response, 'Logout', status_code=200)
        # print("\n" + "="*80)
        # print("REDIRECT CHAIN:")
        # print(response.redirect_chain)
        # print("\nFINAL URL:")
        # print(response.request['PATH_INFO'])
        # print("\nRESPONSE BODY (first 2000 chars):")
        # print(response.content.decode('utf-8')[:2000])
        # print("="*80 + "\n")

    # auth
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_view_home_authenticated(self):

        helper.add_user_with_perm(login="unittest", perms=[
            "p_home_access",
        ])
        response = self.client.get(reverse('app_home:private'), follow=True)

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'unittest', status_code=200)
        self.assertContains(response, 'Welcome', status_code=200)
        self.assertContains(response, 'Sirene', status_code=200)
        self.assertContains(response, 'Logout', status_code=200)



    # HOME - admintools
    # -----------------

    def test_admintools_noauth(self):

        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check that the response contains the DB Reset button
        self.assertContains(response, 'Please sign-in', status_code=200)


    def test_admintools_noperm(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertNotContains(response, 'factoryResetModal', status_code=200)
        self.assertNotContains(response, 'Admin tools', status_code=200)


    def test_admintools_revision_non_admin_user_denied(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools_revision'), follow=True)
        # Should be redirected, not allowed
        self.assertEqual(response.status_code, 200)
        # Should not contain revision page content
        self.assertNotContains(response, 'Data Revisions', status_code=200)

    # ---

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_admintools_access_with_permission(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check that the response contains the DB Reset button
        self.assertContains(response, 'DB Reset', status_code=200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_admintools_modal_present(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check for modal elements
        self.assertContains(response, 'factoryResetModal', status_code=200)
        self.assertContains(response, 'Confirm Factory Reset', status_code=200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_admintools_csrf_token_in_modal_form(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check for CSRF token in the modal's form
        self.assertContains(response, 'csrfmiddlewaretoken', status_code=200)



    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_admintools_revision_access_with_permission(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools_revision'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check for revision page content
        self.assertContains(response, 'Data Revisions', status_code=200)
        # When no revisions exist, check for the empty message
        self.assertContains(response, 'No more revision entries found', status_code=200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_admintools_revision_button_in_admintools_page(self):

        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_home:admintools'), follow=True)
        self.assertEqual(response.status_code, 200)
        # Check for Revision button
        self.assertContains(response, 'Revision', status_code=200)
        # Check for the button URL
        self.assertContains(response, reverse('app_home:admintools_revision'), status_code=200)
        # Check for description
        self.assertContains(response, 'View and manage data revision tracking and history', status_code=200)


    # HOME - logs
    # -----------
    # view, filter, purge

    # HOME - conf
    # -----------
    # CRUD




    # -------------------------------------------------------------
    # IAM
    # -------------------------------------------------------------

    def test_anonymous_public(self):
        response = self.client.get(reverse('app_home:index'))
        self.assertEqual(response.status_code, 302)

        

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_private(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:private'), follow=True)
        self.assertEqual(response.status_code, 200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_debug(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:debug'))
        self.assertEqual(response.status_code, 200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_logout(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:logout'), follow=True)
        self.assertEqual(response.status_code, 200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_list(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:user_list'))
        self.assertEqual(response.status_code, 200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_detail(self):
        helper.add_admin_user(login="unittest")
        # response = self.client.get(
        #   reverse('app_user:user_detail', args=["1"]), follow=True )
        response = self.client.get(
            reverse(
                'app_user:user_detail',
                kwargs={
                    'userid': 1}),
            follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_edit_new(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:user_edit'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_edit_1(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse(
                'app_user:user_edit',
                args=[1]),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')
        # print(response.content)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_edit_post(self):
        helper.add_admin_user(login="unittest")
        # Get the user ID
        user = SireneUser.objects.get(login='unittest')
        user_id = user.id

        # First get the form to obtain CSRF token
        response = self.client.get(
            reverse(
                'app_user:user_edit',
                args=[user_id]),
            follow=True)
        self.assertEqual(response.status_code, 200)

        # POST data to update user
        post_data = {
            'login': 'unittest',
            'firstname': 'Unit',
            'lastname': 'Test',
            'email': 'unittest@example.com',
            'is_enabled': True,
            'is_admin': True,
        }
        response = self.client.post(
            reverse(
                'app_user:user_edit',
                args=[user_id]),
            data=post_data,
            follow=True)
        self.assertEqual(response.status_code, 200)

        # Verify user was updated
        user = SireneUser.objects.get(id=user_id)
        self.assertEqual(user.firstname, 'Unit')
        self.assertEqual(user.lastname, 'Test')
        self.assertEqual(user.email, 'unittest@example.com')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_delete_1(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse(
                'app_user:user_delete',
                args=[1]),
            follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_pref(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:user_pref'), follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_email_test(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse(
                'app_user:email_test',
                args=[1]),
            follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_sms_test(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse(
                'app_user:sms_test',
                args=[1]),
            follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_impersonate(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse('app_user:impersonate'), follow=True)
        self.assertEqual(response.status_code, 200)

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_user_impersonate_testuser01(self):
        helper.add_admin_user(login="unittest")
        helper.add_user(login='testuser01')
        response = self.client.get(
            reverse(
                'app_user:impersonate',
                kwargs={
                    'newlogin': 'testuser01'}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'testuser01')



    # group

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_list(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:group_list'))
        self.assertEqual(response.status_code, 200)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_detail(self):
        helper.add_admin_user(login="unittest")
        group = helper.add_group(name="testgroup01")
        response = self.client.get(
            reverse(
                'app_user:group_detail',
                kwargs={
                    'id': group.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, group.keyname)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_edit_new(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:group_edit'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_edit(self):
        helper.add_admin_user(login="unittest")
        group = helper.add_group(name="testgroup01")
        response = self.client.get(
            reverse(
                'app_user:group_edit',
                kwargs={
                    'id': group.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, group.keyname)
        response = self.client.get(
            reverse(
                'app_user:group_edit',
                kwargs={
                    'id': 99999999}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Not allowed')


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_edit_post(self):
        helper.add_admin_user(login="unittest")
        group = helper.add_group(name="testgroup01")

        # First get the form to obtain CSRF token
        response = self.client.get(
            reverse(
                'app_user:group_edit',
                kwargs={
                    'id': group.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)

        # POST data to update group
        post_data = {
            'keyname': 'testgroup01',
            'description': 'Updated test group',
            'is_enabled': True,
        }
        response = self.client.post(
            reverse(
                'app_user:group_edit',
                kwargs={
                    'id': group.id}),
            data=post_data,
            follow=True)
        self.assertEqual(response.status_code, 200)

        # Verify group was updated
        updated_group = group_get_by_id(group.id)
        self.assertIsNotNone(updated_group)
        self.assertEqual(updated_group.description, 'Updated test group')


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_group_delete_1(self):
        helper.add_admin_user(login="unittest")
        group = helper.add_group(name="testgroup01")
        response = self.client.post(
            reverse(
                'app_user:group_delete',
                kwargs={
                    'id': group.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        group2 = group_get_by_id(group.id)
        self.assertIsNone(group2)



    # role

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_list(self):
        helper.add_admin_user(login="unittest")
        role = helper.add_role(name="testrole01")
        response = self.client.get(reverse('app_user:role_list'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, role.keyname)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_detail(self):
        helper.add_admin_user(login="unittest")
        role = helper.add_role(name="testrole01")
        response = self.client.get(
            reverse(
                'app_user:role_detail',
                kwargs={
                    'id': role.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, role.keyname)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_edit_new(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_user:role_edit'), follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')

    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_edit(self):
        helper.add_admin_user(login="unittest")
        role = helper.add_role(name="testrole01")
        response = self.client.get(
            reverse(
                'app_user:role_edit',
                kwargs={
                    'id': role.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, role.keyname)


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_edit_post(self):
        helper.add_admin_user(login="unittest")
        role = helper.add_role(name="testrole01")

        # First get the form to obtain CSRF token
        response = self.client.get(
            reverse(
                'app_user:role_edit',
                kwargs={
                    'id': role.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)

        # POST data to update role
        post_data = {
            'keyname': 'testrole01',
            'description': 'Updated test role',
            'is_enabled': True,
        }
        response = self.client.post(
            reverse(
                'app_user:role_edit',
                kwargs={
                    'id': role.id}),
            data=post_data,
            follow=True)
        self.assertEqual(response.status_code, 200)

        # Verify role was updated
        updated_role = role_get_by_id(role.id)
        self.assertIsNotNone(updated_role)
        self.assertEqual(updated_role.description, 'Updated test role')


    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_role_delete(self):
        helper.add_admin_user(login="unittest")
        role = helper.add_role(name="testrole01")
        response = self.client.post(
            reverse(
                'app_user:role_delete',
                kwargs={
                    'id': role.id}),
            follow=True)
        # print(response.content)

        self.assertEqual(response.status_code, 200)
        role2 = role_get_by_id(role.id)
        self.assertIsNone(role2)



    # -------------------------------------------------
    # DATA
    # -------------------------------------------------


    # Test authenticated access to data private view
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_data_private(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(reverse('app_data:private'), follow=True)
        self.assertEqual(response.status_code, 200)


    # Test unauthenticated access redirects to login
    @override_settings(CAVALIBA_AUTH_MODE="local")
    def test_data_private_unauthenticated(self):
        self.client.logout()
        response = self.client.get(reverse('app_data:private'))
        self.assertEqual(response.status_code, 302)


    # Test listing site instances
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_instance_list_site(self):
        helper.add_admin_user(login="unittest")
        helper.add_test_sites(count=5)

        response = self.client.get(
            reverse('app_data:instance_list', kwargs={'classname': 'site'}),
            follow=True)
        self.assertEqual(response.status_code, 200)

        self.assertContains(response, 'site01')
        self.assertContains(response, 'site02')
        self.assertContains(response, 'site03')
        self.assertContains(response, 'site04')
        self.assertContains(response, 'site05')


    # Test editing a site instance
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_instance_edit_site(self):
        helper.add_admin_user(login="unittest")
        helper.add_test_sites(count=1)

        from app_data.models import DataInstance
        site = DataInstance.objects.filter(classname='site', keyname='site01').first()
        self.assertIsNotNone(site)

        response = self.client.get(
            reverse('app_data:instance_edit', kwargs={'id': site.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')

        post_data = {
            'keyname': 'site01',
            'displayname': 'Updated Site 01',
            'description': 'Test site description',
        }
        response = self.client.post(
            reverse('app_data:instance_edit', kwargs={'id': site.id}),
            data=post_data,
            follow=True)
        self.assertEqual(response.status_code, 200)

        site_updated = DataInstance.objects.get(id=site.id)
        self.assertEqual(site_updated.displayname, 'Updated Site 01')


    # Test creating a new site instance
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_instance_new_site(self):
        helper.add_admin_user(login="unittest")
        response = self.client.get(
            reverse('app_data:instance_new', kwargs={'classname': 'site'}),
            follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'csrfmiddlewaretoken')

        post_data = {
            'keyname': 'newsite99',
            'displayname': 'New Site 99',
            'description': 'A brand new test site',
        }
        response = self.client.post(
            reverse('app_data:instance_new', kwargs={'classname': 'site'}),
            data=post_data,
            follow=True)
        self.assertEqual(response.status_code, 200)

        from app_data.models import DataInstance
        new_site = DataInstance.objects.filter(classname='site', keyname='newsite99').first()
        self.assertIsNotNone(new_site)
        self.assertEqual(new_site.displayname, 'New Site 99')


    # Test viewing site instance details
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_instance_detail_site(self):
        helper.add_admin_user(login="unittest")
        helper.add_test_sites(count=1)

        from app_data.models import DataInstance
        site = DataInstance.objects.filter(classname='site', keyname='site01').first()
        self.assertIsNotNone(site)

        response = self.client.get(
            reverse('app_data:instance_detail', kwargs={'id': site.id}),
            follow=True)
        self.assertEqual(response.status_code, 200)

        self.assertContains(response, 'site01')
        self.assertContains(response, 'Site 01')


    # Test deleting a site instance
    @override_settings(CAVALIBA_AUTH_MODE="unittest")
    def test_instance_delete_site(self):
        helper.add_admin_user(login="unittest")
        helper.add_test_sites(count=1)

        from app_data.models import DataInstance
        site = DataInstance.objects.filter(classname='site', keyname='site01').first()
        self.assertIsNotNone(site)
        site_id = site.id

        response = self.client.post(
            reverse('app_data:instance_delete', kwargs={'id': site_id}),
            follow=True)
        self.assertEqual(response.status_code, 200)

        deleted_site = DataInstance.objects.filter(id=site_id).first()
        self.assertIsNone(deleted_site)