# services.py
from decimal import Decimal
from collections import defaultdict
from django.db import transaction
from datetime import date
from finance.models import AccountPayableReceivable, DoctorCommission, Currency
from inventory.models import ProductBatches, ConsumerAccount, SaleOrderItems
from django.db.models import Sum
from django.db.models import F, ExpressionWrapper, DecimalField

class SaleOrderService:
    def __init__(self, saleorder):
        self.saleorder = saleorder

    def process(self):
        self._handle_account_receivable()
        self._handle_consumer_account()
        self._handle_doctor_commissions()

    def _handle_account_receivable(self):
        tx_number = f'TX-AR-0{self.saleorder.id}'
        current_date = date.today()
        currency = Currency.objects.first()

        ar = AccountPayableReceivable.objects.filter(tx_number=tx_number).first()
        if ar:
            ar.date = current_date
            ar.description = f'Receivable From Invoice #{self.saleorder.id} Client: {self.saleorder.consumer}'
            ar.amount_in = self.saleorder.grand_total
            ar.currency = currency
            ar.remarks = 'Payment for invoice'
            ar.save()
        else:
            AccountPayableReceivable.objects.create(
                date=current_date,
                account_type='A/R',
                tx_number=tx_number,
                description=f'Receivable From Invoice #{self.saleorder.id} Client: {self.saleorder.consumer}',
                amount_in=self.saleorder.grand_total,
                amount_out=0,
                currency=currency,
                remarks='Payment for invoice'
            )

    def _handle_consumer_account(self):
        """Create or update the consumer account entry for this sale order.

        When editing an existing sale order we must update the original
        consumer account record and cascade the balance changes to all
        subsequent records.
        """
        transaction_id = f"TX#00{self.saleorder.id}"
        debit = Decimal(0)
        credit = self.saleorder.grand_total

        account = ConsumerAccount.objects.filter(
            consumer=self.saleorder.consumer,
            tx_number=transaction_id,
        ).first()

        if account:  # updating existing sale order
            # adjust credit and recalculate current balance
            account.credit = credit
            account.details = f"Credited Bill#: SO00{self.saleorder.id}"
            account.current_balance = account.previous_balance + credit
            account.save()

            # cascade balance updates to later records
            balance = account.current_balance
            for acc in ConsumerAccount.objects.filter(
                consumer=self.saleorder.consumer,
                id__gt=account.id
            ).order_by("id"):
                acc.previous_balance = balance
                balance = balance + acc.credit - acc.debit
                acc.current_balance = balance
                acc.save()
        else:
            prev = (
                ConsumerAccount.objects.filter(consumer=self.saleorder.consumer)
                .order_by("-id")
                .values("current_balance")
                .first()
            )
            previous_balance = prev["current_balance"] if prev else Decimal(0)
            current_balance = previous_balance + credit

            ConsumerAccount.objects.create(
                consumer=self.saleorder.consumer,
                tx_number=transaction_id,
                details=f"Credited Bill#: SO00{self.saleorder.id}",
                previous_balance=previous_balance,
                debit=debit,
                credit=credit,
                current_balance=current_balance,
            )
    def _handle_doctor_commissions(self):
        items = SaleOrderItems.objects.filter(saleorder=self.saleorder.id)
        doctor_data = defaultdict(lambda: {'commission': Decimal(0), 'products': []})

        for item in items:
            if item.doctor_id:
                total_commission = item.commission * item.quantity
                doctor_data[item.doctor_id]['commission'] += total_commission
                product_title = item.product.product.title if hasattr(item.product, 'product') else str(item.product)
                doctor_data[item.doctor_id]['products'].append(
                    f"{product_title} {item.quantity} * {item.commission} = {total_commission}"
                )

        for doctor_id, data in doctor_data.items():
            credit = data['commission']
            previous_balance = DoctorCommission.objects.filter(doctor=doctor_id).order_by('-id').values('current_balance').first()
            previous_balance = previous_balance['current_balance'] if previous_balance else Decimal(0)
            current_balance = previous_balance + credit
            products_text = ", ".join(data['products'])

            DoctorCommission.objects.update_or_create(
                doctor_id=doctor_id,
                details=f'Commission from Invoice #{self.saleorder.id}',
                defaults={
                    'previous_balance': previous_balance,
                    'debit': Decimal(0),
                    'credit': credit,
                    'current_balance': current_balance,
                    'remarks': products_text
                }
            )

            tx_number = f'TX-AP-{self.saleorder.id}-{doctor_id}'
            currency = Currency.objects.first()
            AccountPayableReceivable.objects.update_or_create(
                tx_number=tx_number,
                defaults={
                    'date': date.today(),
                    'account_type': 'A/P',
                    'description': f'Payble From Invoice #{self.saleorder.id} Client: {self.saleorder.consumer} to {doctor_id}',
                    'amount_in': credit,
                    'amount_out': 0,
                    'currency': currency,
                    'remarks': 'Payment for invoice'
                }
            )
