from decimal import Decimal
from rest_framework import serializers
from shop_management.serializers.data.base import DataRootSerializer
from shop_management.models.data.purchase_payment import PurchasePayment
from shop_management.models.data.account_transaction import (
    AccountTransaction,
    AccountTransactionType,
)
from shop_management.models.data.shop_account import Account


class PurchasePaymentSerializer(DataRootSerializer):
    currency_details = serializers.SerializerMethodField()
    purchase_info = serializers.SerializerMethodField()
    account_details = serializers.SerializerMethodField()
    shop = serializers.ReadOnlyField(source="purchase.shop_id")
    shop_name = serializers.ReadOnlyField(source="purchase.shop.name")

    class Meta:
        model = PurchasePayment
        fields = "__all__"
        read_only_fields = ["converted_amount"]

    def get_currency_details(self, obj):
        if obj.currency:
            return {"id": obj.currency.id, "title": obj.currency.title}
        return None

    def get_purchase_info(self, obj):
        if obj.purchase:
            return {
                "id": obj.purchase.id,
                "shop_id": obj.purchase.shop_id,
                "shop": obj.purchase.shop.name,
                "supplier": obj.purchase.supplier.name if obj.purchase.supplier else "Unknown Supplier",
                "total_price": obj.purchase.total_price,
                "total_paid": obj.purchase.total_paid,
                "total_remained_amount": obj.purchase.total_remained_amount,
                "status": obj.purchase.get_status_display(),
            }
        return None

    def get_account_details(self, obj):
        if obj.account:
            return {
                "id": obj.account.id,
                "name": obj.account.name,
                "currency_id": obj.account.currency_id,
                "currency_title": obj.account.currency.title if obj.account.currency else None,
            }
        return None

    @staticmethod
    def _get_or_create_clearing_account(shop, currency):
        """
        Auto-create (or reuse) one clearing account per shop/currency.
        """
        name = f"Purchase {currency.title} Clearing"
        account, _ = Account.objects.get_or_create(
            shop=shop,
            name=name,
            defaults={"currency": currency, "opening_balance": Decimal("0"), "is_system_account": True},
        )
        updates = []
        if account.currency_id != currency.id:
            account.currency = currency
            updates.append("currency")
        if not account.is_system_account:
            account.is_system_account = True
            updates.append("is_system_account")
        if updates:
            account.save(update_fields=updates)
        return account

    @staticmethod
    def _payment_ref_prefix(payment):
        return f"Purchase payment #{payment.id} for purchase #{payment.purchase_id}"

    def _sync_account_transactions(self, payment):
        ref_prefix = self._payment_ref_prefix(payment)
        AccountTransaction.objects.filter(
            description__startswith=ref_prefix
        ).delete()

        # Source account: payment outflow (minus)
        AccountTransaction.objects.create(
            account=payment.account,
            account_currency=payment.account.currency,
            transaction_currency=payment.currency,
            transaction_type=AccountTransactionType.DEBIT,
            amount=payment.amount,
            exchange_rate=payment.exchange_rate,
            converted_amount=payment.converted_amount,
            description=(
                f"{ref_prefix}{f' | Notes: {payment.notes}' if payment.notes else ''}"
            ),
            transaction_date=payment.payment_date.date(),
        )

        # Clearing account: mirror inflow in purchase currency (plus)
        clearing_account = self._get_or_create_clearing_account(
            payment.purchase.shop, payment.currency
        )
        AccountTransaction.objects.create(
            account=clearing_account,
            account_currency=clearing_account.currency,
            transaction_currency=payment.currency,
            transaction_type=AccountTransactionType.CREDIT,
            amount=payment.amount,
            exchange_rate=Decimal("1"),
            converted_amount=payment.amount,
            description=f"{ref_prefix} | Clearing mirror",
            transaction_date=payment.payment_date.date(),
        )

    def validate(self, attrs):
        purchase = attrs.get("purchase") or getattr(self.instance, "purchase", None)
        account = attrs.get("account") or getattr(self.instance, "account", None)
        amount = attrs.get("amount") or getattr(self.instance, "amount", None)
        payment_currency = attrs.get("currency") or getattr(self.instance, "currency", None)
        manual_rate = attrs.get("exchange_rate")

        if not purchase:
            raise serializers.ValidationError({"purchase": "Purchase is required."})
        if not amount or amount <= 0:
            raise serializers.ValidationError({"amount": "Amount must be greater than 0."})

        source_currency = payment_currency or purchase.currency
        if not source_currency:
            raise serializers.ValidationError(
                {"currency": "Purchase payment currency could not be determined."}
            )
        if source_currency.shop_id != purchase.shop_id:
            raise serializers.ValidationError(
                {"currency": "Payment currency must belong to the purchase shop."}
            )

        if account is None:
            raise serializers.ValidationError({"account": "Account is required."})
        if account.shop_id != purchase.shop_id:
            raise serializers.ValidationError(
                {"account": "Selected account must belong to the same shop as purchase."}
            )

        resolved_rate = manual_rate or AccountTransaction.resolve_exchange_rate(
            purchase.shop, source_currency.id, account.currency_id
        )
        if resolved_rate is None:
            raise serializers.ValidationError(
                {
                    "account": (
                        "No exchange rate found between payment currency and account currency."
                    )
                }
            )
        if resolved_rate <= 0:
            raise serializers.ValidationError(
                {"exchange_rate": "Exchange rate must be greater than 0."}
            )

        attrs["currency"] = source_currency
        attrs["exchange_rate"] = resolved_rate
        attrs["converted_amount"] = AccountTransaction.compute_converted_amount(
            amount, resolved_rate
        )
        return attrs

    def create(self, validated_data):
        payment = super().create(validated_data)
        self._sync_account_transactions(payment)
        return payment

    def update(self, instance, validated_data):
        payment = super().update(instance, validated_data)
        self._sync_account_transactions(payment)
        return payment
