from decimal import Decimal
from django.utils import timezone
from rest_framework import serializers
from shop_management.serializers.data.base import DataRootSerializer
from shop_management.models.data.customer_debtor import CustomerDebtor
from shop_management.models.data.account_transaction import (
    AccountTransaction,
    AccountTransactionType,
)


class CustomerDebtorSerializer(DataRootSerializer):
    customer_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()
    account_details = serializers.SerializerMethodField()
    shop = serializers.ReadOnlyField(source="customer.shop_id")
    shop_name = serializers.ReadOnlyField(source="customer.shop.name")
    outstanding_amount = serializers.SerializerMethodField()

    class Meta:
        model = CustomerDebtor
        fields = "__all__"
        read_only_fields = ["converted_amount", "settled_amount"]
        extra_kwargs = {
            "account": {"required": False, "allow_null": True},
        }

    def get_customer_details(self, obj):
        if obj.customer:
            name = (obj.customer.name or "").strip()
            if not name and obj.customer.email:
                name = str(obj.customer.email).split("@")[0].strip()
            if not name and obj.customer.contact:
                name = str(obj.customer.contact).strip()
            if not name:
                name = "Unnamed Customer"
            return {"id": obj.customer.id, "name": name}
        return None

    def get_currency_details(self, obj):
        if obj.currency:
            return {"id": obj.currency.id, "title": obj.currency.title}
        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

    def get_outstanding_amount(self, obj):
        return float(obj.outstanding_amount)

    @staticmethod
    def _debtor_ref_prefix(debtor):
        customer_name = debtor.customer.name if debtor.customer else "Customer"
        return f"Customer loan #{debtor.id} to {customer_name}"

    def _sync_account_transaction(self, debtor):
        if not debtor.customer or not debtor.customer.shop_id:
            raise serializers.ValidationError(
                {"customer": "Selected customer must belong to a shop."}
            )
        if not debtor.currency_id:
            raise serializers.ValidationError({"currency": "Currency is required."})
        if debtor.currency.shop_id != debtor.customer.shop_id:
            raise serializers.ValidationError(
                {"currency": "Currency must belong to the same shop as customer."}
            )

        ref_prefix = self._debtor_ref_prefix(debtor)
        AccountTransaction.objects.filter(description__startswith=ref_prefix).delete()

        # No account selected → do not deduct from any account
        if not debtor.account_id:
            return

        if not debtor.account.currency_id:
            raise serializers.ValidationError(
                {"account": "Selected account must have a currency."}
            )
        if debtor.account.shop_id != debtor.customer.shop_id:
            raise serializers.ValidationError(
                {"account": "Selected account must belong to the same shop as customer."}
            )

        # Money leaves the selected account (debit)
        AccountTransaction.objects.create(
            account=debtor.account,
            account_currency=debtor.account.currency,
            transaction_currency=debtor.currency,
            transaction_type=AccountTransactionType.DEBIT,
            amount=debtor.amount,
            exchange_rate=debtor.exchange_rate,
            converted_amount=debtor.converted_amount,
            description=(
                f"{ref_prefix}"
                f"{f' | Notes: {debtor.notes}' if debtor.notes else ''}"
            ),
            transaction_date=debtor.loan_date.date(),
        )

    def validate(self, attrs):
        loan_date = attrs.get("loan_date")
        if loan_date is not None and timezone.is_naive(loan_date):
            attrs["loan_date"] = timezone.make_aware(
                loan_date, timezone.get_current_timezone()
            )

        customer = attrs.get("customer") or getattr(self.instance, "customer", None)
        currency = attrs.get("currency") or getattr(self.instance, "currency", None)
        if "account" in attrs:
            account = attrs.get("account")
        else:
            account = getattr(self.instance, "account", None)
        amount = attrs.get("amount") or getattr(self.instance, "amount", None)
        manual_rate = attrs.get("exchange_rate")

        if not customer:
            raise serializers.ValidationError({"customer": "Customer is required."})
        if not currency:
            raise serializers.ValidationError({"currency": "Currency is required."})
        if not amount or amount <= 0:
            raise serializers.ValidationError(
                {"amount": "Amount must be greater than 0."}
            )
        if currency.shop_id != customer.shop_id:
            raise serializers.ValidationError(
                {"currency": "Currency must belong to the customer shop."}
            )

        if account is not None:
            if account.shop_id != customer.shop_id:
                raise serializers.ValidationError(
                    {
                        "account": "Selected account must belong to the same shop as customer."
                    }
                )

        if self.instance is not None:
            settled = Decimal(str(self.instance.settled_amount or 0))
            if Decimal(str(amount)) < settled:
                raise serializers.ValidationError(
                    {
                        "amount": (
                            f"Amount cannot be less than already settled amount ({settled})."
                        )
                    }
                )

        if account is None:
            resolved_rate = Decimal(manual_rate or 1)
            if resolved_rate <= 0:
                raise serializers.ValidationError(
                    {"exchange_rate": "Exchange rate must be greater than 0."}
                )
            attrs["exchange_rate"] = resolved_rate
            attrs["converted_amount"] = Decimal(amount)
            return attrs

        resolved_rate = manual_rate or AccountTransaction.resolve_exchange_rate(
            customer.shop, currency.id, account.currency_id
        )
        if resolved_rate is None:
            raise serializers.ValidationError(
                {
                    "account": "No exchange rate found between currency and account currency."
                }
            )
        if resolved_rate <= 0:
            raise serializers.ValidationError(
                {"exchange_rate": "Exchange rate must be greater than 0."}
            )

        attrs["exchange_rate"] = Decimal(resolved_rate)
        attrs["converted_amount"] = AccountTransaction.compute_converted_amount(
            Decimal(amount), Decimal(resolved_rate)
        )
        return attrs

    def create(self, validated_data):
        debtor = super().create(validated_data)
        self._sync_account_transaction(debtor)
        return debtor

    def update(self, instance, validated_data):
        debtor = super().update(instance, validated_data)
        self._sync_account_transaction(debtor)
        return debtor
