"""
Tests for AccountingService - Basic validation tests for task 3.1
"""

from decimal import Decimal
from datetime import date
from django.test import TestCase
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 PostJournalEntryTest(TestCase):
    """Test cases for AccountingService.post_journal_entry method"""

    def setUp(self):
        """Set up test data"""
        # Create test user
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )

        # Create test branch
        self.branch = Branch.objects.create(
            name='Test Branch',
            code='TB001',
            address='Test Address'
        )

        # Create test accounts
        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.expense_account = Account.objects.create(
            code='301001',
            name='Office Expense',
            account_type='expense',
            description='Office expense account',
            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
        )

        # Create an open fiscal period
        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'
        )

        # Initialize service
        self.service = AccountingService()

    def test_post_balanced_journal_entry(self):
        """Test posting a balanced journal entry creates ledger entries"""
        # Create a balanced journal entry
        journal_entry = JournalEntry.objects.create(
            reference_number='JE-001',
            transaction_date=date(2024, 1, 15),
            description='Test journal entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        # Add lines: Debit Cash, Credit Income
        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            account=self.cash_account,
            description='Receipt of income',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )

        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            account=self.income_account,
            description='Income earned',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )

        # Post the journal entry
        result = self.service.post_journal_entry(journal_entry, self.user)

        # Assertions
        self.assertTrue(result)
        journal_entry.refresh_from_db()
        self.assertEqual(journal_entry.status, 'posted')
        self.assertEqual(journal_entry.posted_by, self.user)
        self.assertIsNotNone(journal_entry.posted_at)

        # Check general ledger entries were created
        ledger_entries = GeneralLedger.objects.filter(
            journal_entry=journal_entry
        )
        self.assertEqual(ledger_entries.count(), 2)

        # Check balances
        cash_ledger = ledger_entries.get(account=self.cash_account)
        self.assertEqual(cash_ledger.debit_amount, Decimal('1000.00'))
        self.assertEqual(cash_ledger.balance, Decimal('1000.00'))

        income_ledger = ledger_entries.get(account=self.income_account)
        self.assertEqual(income_ledger.credit_amount, Decimal('1000.00'))
        self.assertEqual(income_ledger.balance, Decimal('1000.00'))

    def test_post_unbalanced_journal_entry_raises_error(self):
        """Test posting an unbalanced journal entry raises ValidationError"""
        # Create an unbalanced journal entry
        journal_entry = JournalEntry.objects.create(
            reference_number='JE-002',
            transaction_date=date(2024, 1, 15),
            description='Unbalanced entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        # Add unbalanced lines
        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            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=journal_entry,
            account=self.income_account,
            description='Credit income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('500.00'),  # Unbalanced!
            line_number=2
        )

        # Attempt to post should raise ValidationError
        with self.assertRaises(ValidationError) as context:
            self.service.post_journal_entry(journal_entry, self.user)

        self.assertIn('Debits must equal credits', str(context.exception))

    def test_post_journal_entry_closed_period_raises_error(self):
        """Test posting to a closed period raises ValidationError"""
        # Close the fiscal period
        self.fiscal_period.status = 'closed'
        self.fiscal_period.save()

        # Create a journal entry
        journal_entry = JournalEntry.objects.create(
            reference_number='JE-003',
            transaction_date=date(2024, 1, 15),
            description='Test entry in closed period',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            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=journal_entry,
            account=self.income_account,
            description='Credit income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )

        # Attempt to post should raise ValidationError
        with self.assertRaises(ValidationError) as context:
            self.service.post_journal_entry(journal_entry, self.user)

        self.assertIn('Period is closed', str(context.exception))

    def test_post_journal_entry_inactive_account_raises_error(self):
        """Test posting with an inactive account raises ValidationError"""
        # Deactivate the cash account
        self.cash_account.is_active = False
        self.cash_account.save()

        # Create a journal entry
        journal_entry = JournalEntry.objects.create(
            reference_number='JE-004',
            transaction_date=date(2024, 1, 15),
            description='Test entry with inactive account',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            account=self.cash_account,  # Inactive!
            description='Debit cash',
            debit_amount=Decimal('1000.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )

        JournalEntryLine.objects.create(
            journal_entry=journal_entry,
            account=self.income_account,
            description='Credit income',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('1000.00'),
            line_number=2
        )

        # Attempt to post should raise ValidationError
        with self.assertRaises(ValidationError) as context:
            self.service.post_journal_entry(journal_entry, self.user)

        self.assertIn('is inactive', str(context.exception))

    def test_balance_calculation_asset_account(self):
        """Test that asset accounts increase with debits and decrease with credits"""
        # Post first entry: Debit cash 1000
        je1 = JournalEntry.objects.create(
            reference_number='JE-005',
            transaction_date=date(2024, 1, 15),
            description='First entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        JournalEntryLine.objects.create(
            journal_entry=je1,
            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=je1,
            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(je1, self.user)

        # Check balance
        ledger1 = GeneralLedger.objects.get(
            journal_entry=je1,
            account=self.cash_account
        )
        self.assertEqual(ledger1.balance, Decimal('1000.00'))

        # Post second entry: Credit cash 300
        je2 = JournalEntry.objects.create(
            reference_number='JE-006',
            transaction_date=date(2024, 1, 16),
            description='Second entry',
            status='draft',
            branch=self.branch,
            created_by=self.user
        )

        JournalEntryLine.objects.create(
            journal_entry=je2,
            account=self.expense_account,
            description='Debit expense',
            debit_amount=Decimal('300.00'),
            credit_amount=Decimal('0.00'),
            line_number=1
        )

        JournalEntryLine.objects.create(
            journal_entry=je2,
            account=self.cash_account,
            description='Credit cash',
            debit_amount=Decimal('0.00'),
            credit_amount=Decimal('300.00'),
            line_number=2
        )

        self.service.post_journal_entry(je2, self.user)

        # Check running balance: 1000 - 300 = 700
        ledger2 = GeneralLedger.objects.get(
            journal_entry=je2,
            account=self.cash_account
        )
        self.assertEqual(ledger2.balance, Decimal('700.00'))

    def test_balance_calculation_income_account(self):
        """Test that income accounts increase with credits and decrease with debits"""
        # Post entry: Credit income 5000
        je = JournalEntry.objects.create(
            reference_number='JE-007',
            transaction_date=date(2024, 1, 15),
            description='Income entry',
            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('5000.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('5000.00'),
            line_number=2
        )

        self.service.post_journal_entry(je, self.user)

        # Check income balance: should be 5000 (credit increases income)
        ledger = GeneralLedger.objects.get(
            journal_entry=je,
            account=self.income_account
        )
        self.assertEqual(ledger.balance, Decimal('5000.00'))
