from django.test import TestCase
from django.utils import timezone
from django.contrib.auth.models import User

from ..models import Member, Group, Freizeit, DIVERSE, GEMEINSCHAFTS_TOUR, MemberTraining, TrainingCategory, LJPProposal
from ..rules import is_oneself, may_view, may_change, may_delete, is_own_training, is_leader_of_excursion, is_leader, statement_not_submitted, _is_leader
from finance.models import Statement
from mailer.models import EmailAddress


class RulesTestCase(TestCase):
    def setUp(self):
        # Create email address for groups
        self.email_address = EmailAddress.objects.create(name='test@example.com')

        # Create test users and members
        self.user1 = User.objects.create_user(username='user1', email='user1@example.com')
        self.member1 = Member.objects.create(
            prename='Test',
            lastname='Member1',
            birth_date=timezone.now().date(),
            email='member1@example.com',
            gender=DIVERSE
        )
        self.user1.member = self.member1
        self.user1.save()

        self.user2 = User.objects.create_user(username='user2', email='user2@example.com')
        self.member2 = Member.objects.create(
            prename='Test',
            lastname='Member2',
            birth_date=timezone.now().date(),
            email='member2@example.com',
            gender=DIVERSE
        )
        self.user2.member = self.member2
        self.user2.save()

        self.user3 = User.objects.create_user(username='user3', email='user3@example.com')
        self.member3 = Member.objects.create(
            prename='Test',
            lastname='Member3',
            birth_date=timezone.now().date(),
            email='member3@example.com',
            gender=DIVERSE
        )
        self.user3.member = self.member3
        self.user3.save()

        # Create test group
        self.group = Group.objects.create(name='Test Group')
        self.group.contact_email = self.email_address
        self.group.leiters.add(self.member2)
        self.group.save()

        # Create test excursion
        self.excursion = Freizeit.objects.create(
            name='Test Excursion',
            tour_type=GEMEINSCHAFTS_TOUR,
            kilometers_traveled=10,
            difficulty=1
        )
        self.excursion.jugendleiter.add(self.member1)
        self.excursion.groups.add(self.group)
        self.excursion.save()

        # Create training category and training
        self.training_category = TrainingCategory.objects.create(
            name='Test Training',
            permission_needed=False
        )

        self.training = MemberTraining.objects.create(
            member=self.member1,
            title='Test Training',
            category=self.training_category,
            participated=True,
            passed=True
        )

        # Create LJP proposal
        self.ljp_proposal = LJPProposal.objects.create(
            title='Test LJP',
            excursion=self.excursion
        )

        # Create statement
        self.statement_unsubmitted = Statement.objects.create(
            short_description='Unsubmitted Statement',
            excursion=self.excursion,
            status=Statement.UNSUBMITTED
        )

        self.statement_submitted = Statement.objects.create(
            short_description='Submitted Statement',
            status=Statement.SUBMITTED
        )

    def test_is_oneself(self):
        """Test is_oneself rule - member can identify themselves."""
        # Same member
        self.assertTrue(is_oneself(self.user1, self.member1))

        # Different members
        self.assertFalse(is_oneself(self.user1, self.member2))

    def test_may(self):
        """Test `may_` rules."""
        self.assertTrue(may_view(self.user1, self.member1))
        self.assertTrue(may_change(self.user1, self.member1))
        self.assertTrue(may_delete(self.user1, self.member1))

    def test_is_own_training(self):
        """Test is_own_training rule - member can access their own training."""
        # Own training
        self.assertTrue(is_own_training(self.user1, self.training))
        # Other member's training
        self.assertFalse(is_own_training(self.user2, self.training))

    def test_is_leader_of_excursion(self):
        """Test is_leader_of_excursion rule for LJP proposals."""
        # LJP proposal with excursion - member3 is not a leader
        self.assertFalse(is_leader_of_excursion(self.user3, self.ljp_proposal))
        # Directly pass an excursion
        self.assertTrue(is_leader_of_excursion(self.user1, self.excursion))

    def test_is_leader(self):
        """Test is_leader rule for excursions."""
        # Direct excursion leader
        self.assertTrue(is_leader(self.user1, self.excursion))

        # Group leader (member2 is leader of group that is part of excursion)
        self.assertTrue(is_leader(self.user2, self.excursion))

        # member3 is unrelated
        self.assertFalse(is_leader(self.user3, self.excursion))

        # Test user without member attribute
        user_no_member = User.objects.create_user(username='nomember', email='nomember@example.com')
        self.assertFalse(is_leader(user_no_member, self.excursion))

        # Test member without pk attribute
        class MemberNoPk:
            pass
        member_no_pk = MemberNoPk()
        self.assertFalse(_is_leader(member_no_pk, self.excursion))

        # Test member with None pk
        class MemberNonePk:
            pk = None
        member_none_pk = MemberNonePk()
        self.assertFalse(_is_leader(member_none_pk, self.excursion))

    def test_statement_not_submitted(self):
        """Test statement_not_submitted rule."""
        # Unsubmitted statement with excursion
        self.assertTrue(statement_not_submitted(self.user1, self.excursion))

        # Submitted statement
        self.excursion.statement = self.statement_submitted
        self.excursion.save()
        self.assertFalse(statement_not_submitted(self.user1, self.excursion))

        # Excursion without statement
        excursion_no_statement = Freizeit.objects.create(
            name='No Statement Excursion',
            tour_type=GEMEINSCHAFTS_TOUR,
            kilometers_traveled=10,
            difficulty=1
        )
        self.assertFalse(statement_not_submitted(self.user1, excursion_no_statement))

        # Test the excursion.statement is None case
        # Create a special test object to directly trigger
        class ExcursionWithNoneStatement:
            def __init__(self):
                self.statement = None
        # if excursion.statement is None: return False
        self.assertFalse(statement_not_submitted(self.user1, ExcursionWithNoneStatement()))
