from decimal import Decimal

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIClient

from shop_management.models.data.accounting import JournalEntry, JournalEntryLine, LedgerAccount
from shop_management.models.data.shop import Shop
from shop_management.models.data.sale import Sale
from shop_management.models.data.utils import Currency
from shop_management.services.chart_of_accounts import initialize_standard_chart


class AccountingReportTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_superuser(username="report-admin", password="test")
        self.shop = Shop.objects.create(owner=self.user, name="Kabul")
        self.currency = Currency.objects.create(shop=self.shop, title="AFN")
        sale = Sale.objects.create(
            shop=self.shop,
            currency=self.currency,
            sale_number="INV-100",
            uses_inventory_engine=True,
            document_status=Sale.DocumentStatus.POSTED,
        )
        initialize_standard_chart(self.shop, user=self.user)
        entry = JournalEntry.objects.create(shop=self.shop, entry_date=timezone.localdate(), description="Sale INV-100", source_type="SALE", source_id=str(sale.pk), status=JournalEntry.Status.POSTED, journal_number="BR1-JRN-TEST-1", created_by=self.user)
        cash = LedgerAccount.objects.get(shop=self.shop, code="1100")
        revenue = LedgerAccount.objects.get(shop=self.shop, code="4100")
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=cash, transaction_currency=self.currency, debit=1000, base_debit=1000, exchange_rate_used=1)
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=revenue, transaction_currency=self.currency, credit=1000, base_credit=1000, exchange_rate_used=1)
        self.client = APIClient()
        self.client.force_authenticate(self.user)

    def test_trial_balance_is_balanced(self):
        response = self.client.get("/api/shop/accounting-reports/", {"report": "trial_balance", "shop": self.shop.pk})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["total_debit"], "1000.00")
        self.assertEqual(response.data["total_credit"], "1000.00")

    def test_profit_and_loss_uses_posted_journal_lines(self):
        response = self.client.get("/api/shop/accounting-reports/", {"report": "profit_loss", "shop": self.shop.pk})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["income"], "1000.00")
        self.assertEqual(response.data["net_profit"], "1000.00")

    def test_profit_and_loss_keeps_transaction_currencies_separate(self):
        usd = Currency.objects.create(shop=self.shop, title="USD")
        entry = JournalEntry.objects.create(shop=self.shop, entry_date=timezone.localdate(), description="USD sale", status=JournalEntry.Status.POSTED, journal_number="BR1-JRN-TEST-USD", created_by=self.user)
        revenue = LedgerAccount.objects.get(shop=self.shop, code="4100")
        cash = LedgerAccount.objects.get(shop=self.shop, code="1100")
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=cash, transaction_currency=usd, debit=10, base_debit=700, exchange_rate_used=70)
        JournalEntryLine.objects.create(journal_entry=entry, ledger_account=revenue, transaction_currency=usd, credit=10, base_credit=700, exchange_rate_used=70)

        response = self.client.get("/api/shop/accounting-reports/", {"report": "profit_loss", "shop": self.shop.pk})
        self.assertEqual(response.status_code, 200)
        summaries = {row["currency"]: row for row in response.data["currency_summaries"]}
        self.assertEqual(summaries["AFN"]["income"], "1000.00")
        self.assertEqual(summaries["USD"]["income"], "10.00")
        self.assertEqual({row["currency"] for row in response.data["rows"]}, {"AFN", "USD"})

    def test_general_ledger_groups_account_lines_by_invoice_or_payment(self):
        response = self.client.get(
            "/api/shop/accounting-reports/",
            {"report": "general_ledger", "shop": self.shop.pk},
        )

        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data["rows"]), 1)
        row = response.data["rows"][0]
        self.assertEqual(row["document_type"], "SALE")
        self.assertEqual(row["document_reference"], "INV-100")
        self.assertEqual(row["description"], "Sale INV-100")
        self.assertEqual(row["debit"], "1000.00")
        self.assertEqual(row["credit"], "1000.00")
        self.assertEqual(row["debit_accounts"], ["1100 · Cash"])
        self.assertEqual(row["credit_accounts"], ["4100 · Sales Revenue"])

    def test_account_activity_remains_line_wise_with_running_balance(self):
        cash = LedgerAccount.objects.get(shop=self.shop, code="1100")
        response = self.client.get(
            "/api/shop/accounting-reports/",
            {"report": "account_activity", "shop": self.shop.pk, "ledger_account": cash.pk},
        )

        self.assertEqual(response.status_code, 200, response.data)
        self.assertEqual(len(response.data["rows"]), 1)
        self.assertEqual(response.data["rows"][0]["running_balance"], "1000.00")
