from datetime import date

from django.db.models import Q
from freezegun import freeze_time
from django.conf import settings
from django.test import SimpleTestCase, TestCase
from django.utils import timezone, translation
from accrete.forms import TenantModelForm
from accrete.utils import dates, forms, models, views
from accrete.models import AccessGroup, Member, Tenant


class DatesSimpleTestCase(SimpleTestCase):

    @freeze_time('2026-03-03')
    def test_current_year_start(self):
        self.assertEqual(
            dates.current_year_start(),
            timezone.now().replace(
                year=2026, month=1, day=1, hour=0, second=0, microsecond=0
            )
        )

    @freeze_time('2026-03-03')
    def test_current_year_end(self):
        self.assertEqual(
            dates.current_year_end(),
            timezone.now().replace(
                year=2026,
                month=12,
                day=31,
                hour=23,
                minute=59,
                second=59,
                microsecond=999999,
            )
        )

    @freeze_time('2026-03-03')
    def test_current_month_start(self):
        self.assertEqual(
            dates.current_month_start(),
            timezone.now().replace(
                year=2026, month=3, day=1, hour=0, second=0, microsecond=0
            )
        )

    @freeze_time('2026-02-03')
    def test_current_month_end(self):
        self.assertEqual(
            dates.current_month_end(),
            timezone.now().replace(
                year=2026,
                month=2,
                day=28,
                hour=23,
                minute=59,
                second=59,
                microsecond=999999,
            )
        )

    @freeze_time('2026-03-03', tz_offset=0)
    def test_next_relative_month_start(self):
        self.assertEqual(
            dates.next_relative_month_start(date.today()),
            date(year=2026, month=4, day=1)
        )

    @freeze_time('2026-03-03')
    def test_next_relative_year_start(self):
        self.assertEqual(
            dates.next_relative_year_start(date.today()),
            date(year=2027, month=1, day=1)
        )

    @freeze_time('2026-03-03')
    def test_day_of_month_or_last(self):
        self.assertEqual(
            dates.day_of_month_or_last(2026, 3, 3),
            3
        )

    @freeze_time('2026-02-28')
    def test_day_of_month_or_last(self):
        self.assertEqual(
            dates.day_of_month_or_last(2026, 2, 29),
            28
        )

    @freeze_time('2024-02-29')
    def test_day_of_month_or_last_leap(self):
        self.assertEqual(
            dates.day_of_month_or_last(2024, 2, 29),
            29
        )


class FormsTestCase(TestCase):

    class TestModelForm(TenantModelForm):

        class Meta:
            model = AccessGroup
            fields = [
                'name',
                'description',
                'code',
                'apply_on'
            ]

    def test_save_form_valid(self):
        data = {
            'name': 'Test',
            'description': 'Test Group',
            'code': 'test',
            'apply_on': 'tenant',
        }
        result = forms.save_form(self.TestModelForm(data=data))
        self.assertEqual(True, result.is_saved)

    def test_save_form_invalid(self):
        data = {
            'description': 'Test Group',
            'code': 'test',
            'apply_on': 'tenant',
        }
        result = forms.save_form(self.TestModelForm(data=data))
        self.assertEqual(False, result.is_saved)


class ModelTestCase(TestCase):

    def test_get_related_model(self):
        translation.activate('en')
        result = models.get_related_model(Member, 'tenant__access_groups')
        self.assertEqual(result, (AccessGroup, ['Member', 'Tenant', 'Access Group']))

    def test_get_related_field(self):
        result = models.get_related_field(Member, 'tenant__access_groups__name')
        self.assertEqual(result, AccessGroup._meta.get_field('name'))
    
    def test_translated_db_value(self):
        translation.activate(settings.LANGUAGE_CODE)
        group = AccessGroup(name='Test', code='test')
        group.save()
        result = models.translated_db_value(instance=group, field='name')
        self.assertDictEqual(result, {settings.LANGUAGE_CODE: 'Test'})

    def test_model_to_dict(self):
        translation.activate(settings.LANGUAGE_CODE)
        group = AccessGroup(name='Test', description='Test Group',  code='test')
        group.save()
        result = models.model_to_dict(
            instance=group, 
            fields=['name', 'description', 'code'], 
            translated=False,
        )
        self.assertDictEqual(result, {
            'name': {settings.LANGUAGE_CODE: 'Test'},
            'description': {settings.LANGUAGE_CODE: 'Test Group'},
            'code': 'test',
        })

    def test_model_to_dict_translated(self):
        translation.activate(settings.LANGUAGE_CODE)
        group = AccessGroup(name='Test', description='Test Group',  code='test')
        group.save()
        result = models.model_to_dict(
            instance=group, 
            fields=['name', 'description', 'code'], 
            translated=True,
        
        )
        self.assertDictEqual(result, {
            'name': 'Test',
            'description': 'Test Group',
            'code': 'test',
        })


class ViewTestCase(TestCase):
    
    def test_page_from_querystring(self):
        group = AccessGroup(name='Test', description='Test Group',  code='test')
        group.save()
        result = views.page_from_querystring(
            AccessGroup, query_dict={'q': '[{"code": "test"}]'}
        )
        self.assertQuerySetEqual(
            result.object_list, AccessGroup.objects.filter(code='test')
        )

    def test_parse_querystring(self):
        query = '[{"is_active": true}, "|", {"tenant__name__iexact": "Test Tenant"}]'
        self.assertEqual(
            views.parse_querystring(Member, query),
            Q(is_active=True) | Q(tenant__name__iexact='Test Tenant')
        )
        query = '[{"is_active": true},  "|", [{"is_active": false}, {"tenant__name__startswith": "Test"}]]'
        self.assertEqual(
            views.parse_querystring(Member, query),
            Q(is_active=True) | Q(is_active=False, tenant__name__startswith='Test')
        )
