from decimal import Decimal

from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.test import TestCase
from django.utils import timezone

from shop_management.models.data.accounting import JournalEntry, JournalEntryLine, LedgerAccount
from shop_management.models.data.shop import Shop
from shop_management.models.data.utils import Currency
from shop_management.services.chart_of_accounts import initialize_standard_chart
from shop_management.services.journals import post_journal, void_journal


class JournalEntryLifecycleTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username="journal-admin", password="test")
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        initialize_standard_chart(self.shop, user=self.user)
        self.cash = LedgerAccount.objects.get(shop=self.shop, code="1100")
        self.equity = LedgerAccount.objects.get(shop=self.shop, code="3100")

    def entry(self, credit="1000"):
        entry = JournalEntry.objects.create(shop=self.shop, entry_date=timezone.localdate(), description="Opening cash", created_by=self.user)
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=self.cash, transaction_currency=self.currency, debit=Decimal("1000"), exchange_rate_used=1)
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=self.equity, transaction_currency=self.currency, credit=Decimal(credit), exchange_rate_used=1)
        return entry

    def test_balanced_journal_posts_and_gets_number(self):
        posted = post_journal(self.entry().pk, user=self.user)
        self.assertEqual(posted.status, JournalEntry.Status.POSTED)
        self.assertTrue(posted.journal_number.startswith(f"BR{self.shop.pk}-JRN-"))
        self.assertEqual(posted.lines.aggregate_total if hasattr(posted.lines, "aggregate_total") else posted.lines.count(), 2)
        self.assertEqual(posted.lines.first().base_debit, Decimal("1000.00"))

    def test_unbalanced_journal_is_rejected(self):
        entry = self.entry(credit="900")
        with self.assertRaises(ValidationError):
            post_journal(entry.pk, user=self.user)
        entry.refresh_from_db()
        self.assertEqual(entry.status, JournalEntry.Status.DRAFT)

    def test_posted_journal_can_be_voided_with_reason(self):
        posted = post_journal(self.entry().pk, user=self.user)
        voided = void_journal(posted.pk, user=self.user, reason="Entered twice")
        self.assertEqual(voided.status, JournalEntry.Status.VOID)
        self.assertEqual(voided.void_reason, "Entered twice")
