"""
Unit tests for account forms in the accounting module.
"""

from django.test import TestCase
from django.contrib.auth import get_user_model

from accounting.models import Account
from accounting.forms import AccountForm, AccountFilterForm
from users.models import Branch

User = get_user_model()


class AccountFormTestCase(TestCase):
    """Test cases for AccountForm."""
    
    def setUp(self):
        """Set up test data."""
        # Create test branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Address'
        )
        
        # Create test user
        self.user = User.objects.create_user(
            username='testuser',
            email='test@test.com',
            password='testpass123',
            role='admin',
            branch=self.branch
        )
        
        # Create test accounts
        self.asset_account = Account.objects.create(
            code='101001',
            name='Cash Account',
            account_type='asset',
            subtype='current_asset',
            description='Main cash account',
            is_active=True,
            created_by=self.user
        )
        
        self.parent_account = Account.objects.create(
            code='200000',
            name='Liabilities',
            account_type='liability',
            description='All liabilities',
            is_active=True,
            created_by=self.user
        )
    
    def test_account_form_valid_data(self):
        """Test AccountForm with valid data."""
        form_data = {
            'code': '102001',
            'name': 'Bank Account',
            'account_type': 'asset',
            'subtype': 'current_asset',
            'description': 'Main bank account',
            'is_active': True,
        }
        
        form = AccountForm(data=form_data)
        self.assertTrue(form.is_valid())
    
    def test_account_form_missing_required_fields(self):
        """Test AccountForm with missing required fields."""
        form_data = {
            'code': '102001',
            # Missing name
            'account_type': 'asset',
        }
        
        form = AccountForm(data=form_data)
        self.assertFalse(form.is_valid())
        self.assertIn('name', form.errors)
    
    def test_account_form_duplicate_code(self):
        """Test that form rejects duplicate account codes."""
        form_data = {
            'code': self.asset_account.code,  # Duplicate
            'name': 'Another Account',
            'account_type': 'asset',
            'description': 'Test account',
            'is_active': True,
        }
        
        form = AccountForm(data=form_data)
        self.assertFalse(form.is_valid())
        self.assertIn('code', form.errors)
        self.assertIn('already in use', str(form.errors['code']))
    
    def test_account_form_with_parent_same_type(self):
        """Test AccountForm with parent account of same type."""
        form_data = {
            'code': '201001',
            'name': 'Client Savings',
            'account_type': 'liability',
            'subtype': 'client_savings',
            'description': 'Client savings accounts',
            'parent_account': self.parent_account.id,
            'is_active': True,
        }
        
        form = AccountForm(data=form_data)
        self.assertTrue(form.is_valid())
    
    def test_account_form_parent_different_type(self):
        """Test that form rejects parent account of different type."""
        form_data = {
            'code': '102001',
            'name': 'Bank Account',
            'account_type': 'asset',
            'description': 'Main bank account',
            'parent_account': self.parent_account.id,  # Liability parent for asset
            'is_active': True,
        }
        
        form = AccountForm(data=form_data)
        self.assertFalse(form.is_valid())
        self.assertIn('parent_account', form.errors)
        self.assertIn('same type', str(form.errors['parent_account']))
    
    def test_account_form_self_referencing_parent(self):
        """Test that form prevents self-referencing parent."""
        form_data = {
            'code': self.asset_account.code,
            'name': self.asset_account.name,
            'account_type': self.asset_account.account_type,
            'description': self.asset_account.description,
            'parent_account': self.asset_account.id,  # Self reference
            'is_active': True,
        }
        
        form = AccountForm(data=form_data, instance=self.asset_account)
        self.assertFalse(form.is_valid())
        self.assertIn('parent_account', form.errors)
    
    def test_account_form_circular_reference(self):
        """Test that form prevents circular references."""
        # Create a child account
        child = Account.objects.create(
            code='101002',
            name='Petty Cash',
            account_type='asset',
            description='Petty cash account',
            parent_account=self.asset_account,
            is_active=True,
            created_by=self.user
        )
        
        # Try to make parent a child of its own child
        # The form should exclude this from parent choices, so it will be an invalid choice error
        form_data = {
            'code': self.asset_account.code,
            'name': self.asset_account.name,
            'account_type': self.asset_account.account_type,
            'description': self.asset_account.description,
            'parent_account': child.id,  # Circular reference - should be filtered out
            'is_active': True,
        }
        
        form = AccountForm(data=form_data, instance=self.asset_account)
        self.assertFalse(form.is_valid())
        self.assertIn('parent_account', form.errors)
        # The error will be about invalid choice since child is excluded from queryset
    
    def test_account_form_invalid_subtype_for_type(self):
        """Test that form validates subtype matches account type."""
        form_data = {
            'code': '102001',
            'name': 'Bank Account',
            'account_type': 'income',  # Income doesn't have subtypes
            'subtype': 'current_asset',  # Asset subtype
            'description': 'Test',
            'is_active': True,
        }
        
        form = AccountForm(data=form_data)
        self.assertFalse(form.is_valid())
        self.assertIn('subtype', form.errors)
    
    def test_account_form_asset_with_valid_subtype(self):
        """Test AccountForm with valid asset subtype."""
        code_counter = 1
        for subtype_code, subtype_name in Account.ASSET_SUBTYPES:
            form_data = {
                'code': f'10{code_counter:04d}',  # Generate numeric codes like 100001, 100002, etc.
                'name': f'Test {subtype_name}',
                'account_type': 'asset',
                'subtype': subtype_code,
                'description': 'Test account',
                'is_active': True,
            }
            
            form = AccountForm(data=form_data)
            if not form.is_valid():
                print(f"Form errors for subtype {subtype_code}: {form.errors}")
            self.assertTrue(form.is_valid(), 
                f"Form should be valid for asset subtype {subtype_code}. Errors: {form.errors}")
            code_counter += 1
    
    def test_account_form_liability_with_valid_subtype(self):
        """Test AccountForm with valid liability subtype."""
        code_counter = 1
        for subtype_code, subtype_name in Account.LIABILITY_SUBTYPES:
            form_data = {
                'code': f'20{code_counter:04d}',  # Generate numeric codes like 200001, 200002, etc.
                'name': f'Test {subtype_name}',
                'account_type': 'liability',
                'subtype': subtype_code,
                'description': 'Test account',
                'is_active': True,
            }
            
            form = AccountForm(data=form_data)
            self.assertTrue(form.is_valid(), 
                f"Form should be valid for liability subtype {subtype_code}")
            code_counter += 1
    
    def test_account_form_edit_preserves_code_uniqueness(self):
        """Test that editing doesn't trigger duplicate code error for same account."""
        form_data = {
            'code': self.asset_account.code,  # Same code
            'name': 'Updated Name',
            'account_type': self.asset_account.account_type,
            'description': 'Updated description',
            'is_active': True,
        }
        
        form = AccountForm(data=form_data, instance=self.asset_account)
        self.assertTrue(form.is_valid())
    
    def test_account_form_excludes_self_from_parent_choices(self):
        """Test that form excludes the account itself from parent choices."""
        form = AccountForm(instance=self.asset_account)
        
        # Get parent_account queryset
        parent_choices = form.fields['parent_account'].queryset
        
        # Verify self is not in choices
        self.assertNotIn(self.asset_account, parent_choices)
    
    def test_account_form_excludes_children_from_parent_choices(self):
        """Test that form excludes child accounts from parent choices."""
        # Create a child account
        child = Account.objects.create(
            code='101002',
            name='Petty Cash',
            account_type='asset',
            description='Petty cash account',
            parent_account=self.asset_account,
            is_active=True,
            created_by=self.user
        )
        
        form = AccountForm(instance=self.asset_account)
        
        # Get parent_account queryset
        parent_choices = form.fields['parent_account'].queryset
        
        # Verify child is not in choices
        self.assertNotIn(child, parent_choices)
    
    def test_account_form_initial_is_active_true(self):
        """Test that is_active defaults to True for new accounts."""
        form = AccountForm()
        self.assertTrue(form.initial.get('is_active', False))


class AccountFilterFormTestCase(TestCase):
    """Test cases for AccountFilterForm."""
    
    def test_filter_form_empty_data(self):
        """Test filter form with empty data is valid."""
        form = AccountFilterForm(data={})
        self.assertTrue(form.is_valid())
    
    def test_filter_form_with_search(self):
        """Test filter form with search term."""
        form = AccountFilterForm(data={'search': 'Cash'})
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['search'], 'Cash')
    
    def test_filter_form_with_account_type(self):
        """Test filter form with account type."""
        form = AccountFilterForm(data={'account_type': 'asset'})
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['account_type'], 'asset')
    
    def test_filter_form_with_status(self):
        """Test filter form with status."""
        form = AccountFilterForm(data={'status': 'active'})
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['status'], 'active')
    
    def test_filter_form_with_parent_only(self):
        """Test filter form with parent_only option."""
        form = AccountFilterForm(data={'parent_only': True})
        self.assertTrue(form.is_valid())
        self.assertTrue(form.cleaned_data['parent_only'])
    
    def test_filter_form_all_fields(self):
        """Test filter form with all fields filled."""
        form_data = {
            'search': 'test',
            'account_type': 'liability',
            'status': 'inactive',
            'parent_only': True,
        }
        
        form = AccountFilterForm(data=form_data)
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['search'], 'test')
        self.assertEqual(form.cleaned_data['account_type'], 'liability')
        self.assertEqual(form.cleaned_data['status'], 'inactive')
        self.assertTrue(form.cleaned_data['parent_only'])
