"""
Tests for AccountingService - Comprehensive integration tests
"""

from decimal import Decimal
from datetime import date
from django.test import TestCase, TransactionTestCase
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError

from accounting.models import (
    Account,
    JournalEntry,
    JournalEntryLine,
    GeneralLedger,
    FiscalPeriod,
)
from accounting.services.accounting_service import AccountingService
from users.models import Branch

User = get_user_model()


class ReverseJournalEntryTest(TransactionTestCase):
    """Test cases for AccountingService.reverse_journal_entry method"""

    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )

        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Address'
        )

        self.cash_account = Account.objects.create(
            code='202001',
            name='Cash Account',
            account_type='asset',
            subtype='current_asset',
            description='Cash account for testing',
            is_active=True,
            created_by=self.user
        )

        self.income_account = Account.objects.create(
            code='401001',
            name='Interest Income',
            account_type='income',
            description='Interest income account',
            is_active=True,
            created_by=self.user
        )

        self.fiscal_period = FiscalPeriod.objects.create(
            name='January 2024',
            period_type='monthly',
            start_date=date(2024, 1, 1),
            end_date=date(2024, 1, 31),
            status='open'
        )

        self.service = AccountingService()

    def test_reverse_journal_entry_creates_reversal_and_updates_original(self):
        """Test reversing a journal entry creates reversal and updates original"""
        # Create and post original entry
        original = JournalEntry.objects.create(
            reference_number='JE-008',
            transaction_date=date(2024, 1, 15),
            description='Original entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        JournalEntryLine.objects.create(
            journal_entry=original,
            account=self.cash_account,
            description='Debit cash',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )

        JournalEntryLine.objects.create(
            journal_entry=original,
            account=self.income_account,
            description='Credit income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )

        self.service.post_journal_entry(original, self.user)

        # Reverse the entry
        reversal = self.service.reverse_journal_entry(
            original, self.user, date(2024, 1, 16), 'Error correction'
        )

        # Check reversal entry
        self.assertEqual(reversal.reference_number, 'JE-008-REV')
        self.assertEqual(reversal.status, 'posted')
        self.assertEqual(reversal.reverses, original)
        self.assertIn('REVERSAL', reversal.description)

        # Check original entry was marked as reversed
        original.refresh_from_db()
        self.assertEqual(original.status, 'reversed')

        # Check reversal lines have swapped debits/credits
        reversal_lines = reversal.lines.all()
        self.assertEqual(reversal_lines.count(), 2)

        cash_line = reversal_lines.get(account=self.cash_account)
        self.assertEqual(cash_line.credit_amount, Decimal('1000.00'))
        self.assertEqual(cash_line.debit_amount, Decimal('0.00'))

        income_line = reversal_lines.get(account=self.income_account)
        self.assertEqual(income_line.debit_amount, Decimal('1000.00'))
        self.assertEqual(income_line.credit_amount, Decimal('0.00'))

        # Check ledger balances are zeroed out
        cash_balance = self.service.calculate_account_balance(
            self.cash_account, date(2024, 1, 16), self.branch
        )
        self.assertEqual(cash_balance, Decimal('0.00'))

    def test_reverse_draft_entry_raises_error(self):
        """Test reversing a draft entry raises ValidationError"""
        draft_entry = JournalEntry.objects.create(
            reference_number='JE-009',
            transaction_date=date(2024, 1, 15),
            description='Draft entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        with self.assertRaises(ValidationError) as context:
            self.service.reverse_journal_entry(
                draft_entry, self.user, date(2024, 1, 16), 'Test'
            )

        self.assertIn('Only posted journal entries can be reversed', str(context.exception))



class CalculateAccountBalanceTest(TransactionTestCase):
    """Test cases for AccountingService.calculate_account_balance method"""

    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )

        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Address'
        )

        self.cash_account = Account.objects.create(
            code='202001',
            name='Cash Account',
            account_type='asset',
            subtype='current_asset',
            description='Cash account for testing',
            is_active=True,
            created_by=self.user
        )

        self.income_account = Account.objects.create(
            code='401001',
            name='Interest Income',
            account_type='income',
            description='Interest income account',
            is_active=True,
            created_by=self.user
        )

        self.fiscal_period = FiscalPeriod.objects.create(
            name='January 2024',
            period_type='monthly',
            start_date=date(2024, 1, 1),
            end_date=date(2024, 1, 31),
            status='open'
        )

        self.service = AccountingService()

    def test_calculate_account_balance_returns_correct_balance(self):
        """Test calculating account balance returns correct value"""
        # Post multiple entries
        for i in range(1, 4):
            je = JournalEntry.objects.create(
                reference_number=f'JE-{i:03d}',
                transaction_date=date(2024, 1, 10 + i),
                description=f'Entry {i}',
                status='draft',
                branch=self.branch,
                created_by=self.user
            )

            JournalEntryLine.objects.create(
                journal_entry=je,
                account=self.cash_account,
                description='Debit cash',
                debit_amount=Decimal('100.00'),
                credit_amount=Decimal('0.00'),
                line_number=1
            )

            JournalEntryLine.objects.create(
                journal_entry=je,
                account=self.income_account,
                description='Credit income',
                debit_amount=Decimal('0.00'),
                credit_amount=Decimal('100.00'),
                line_number=2
            )

            self.service.post_journal_entry(je, self.user)

        # Calculate balance
        balance = self.service.calculate_account_balance(
            self.cash_account, date(2024, 1, 13), self.branch
        )

        # Should be 300.00 (3 entries x 100.00)
        self.assertEqual(balance, Decimal('300.00'))

    def test_calculate_account_balance_zero_for_no_transactions(self):
        """Test calculating balance returns zero for accounts with no transactions"""
        balance = self.service.calculate_account_balance(
            self.cash_account, date(2024, 1, 15), self.branch
        )
        self.assertEqual(balance, Decimal('0.00'))


class GetTrialBalanceTest(TransactionTestCase):
    """Test cases for AccountingService.get_trial_balance method"""

    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )

        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Address'
        )

        # Create accounts
        self.cash_account = Account.objects.create(
            code='202001',
            name='Cash Account',
            account_type='asset',
            subtype='current_asset',
            description='Cash account',
            is_active=True,
            created_by=self.user
        )

        self.expense_account = Account.objects.create(
            code='301001',
            name='Office Expense',
            account_type='expense',
            description='Office expense',
            is_active=True,
            created_by=self.user
        )

        self.income_account = Account.objects.create(
            code='401001',
            name='Interest Income',
            account_type='income',
            description='Interest income',
            is_active=True,
            created_by=self.user
        )

        self.liability_account = Account.objects.create(
            code='501001',
            name='Client Savings',
            account_type='liability',
            subtype='client_savings',
            description='Client savings',
            is_active=True,
            created_by=self.user
        )

        self.fiscal_period = FiscalPeriod.objects.create(
            name='January 2024',
            period_type='monthly',
            start_date=date(2024, 1, 1),
            end_date=date(2024, 1, 31),
            status='open'
        )

        self.service = AccountingService()

    def test_get_trial_balance_returns_balanced_report(self):
        """Test trial balance report is balanced"""
        # Create balanced entries
        je = JournalEntry.objects.create(
            reference_number='JE-010',
            transaction_date=date(2024, 1, 15),
            description='Test entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        # Debit cash 1000
        JournalEntryLine.objects.create(
            journal_entry=je,
            account=self.cash_account,
            description='Debit cash',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )

        # Credit income 1000
        JournalEntryLine.objects.create(
            journal_entry=je,
            account=self.income_account,
            description='Credit income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )

        self.service.post_journal_entry(je, self.user)

        # Get trial balance
        trial_balance = self.service.get_trial_balance(date(2024, 1, 15), self.branch)

        # Check structure
        self.assertIn('report_date', trial_balance)
        self.assertIn('accounts', trial_balance)
        self.assertIn('totals', trial_balance)
        self.assertIn('by_type', trial_balance)

        # Check totals are balanced
        self.assertTrue(trial_balance['totals']['balanced'])
        self.assertEqual(
            trial_balance['totals']['total_debits'],
            trial_balance['totals']['total_credits']
        )

        # Check account count (should have 2 accounts with balances)
        self.assertEqual(len(trial_balance['accounts']), 2)

        # Check by_type breakdown
        self.assertIn('asset', trial_balance['by_type'])
        self.assertIn('income', trial_balance['by_type'])
