from rest_framework import serializers

from shop_management.models.data.supplier_payment import SupplierPayment, SupplierPaymentAllocation
from account.permissions import assert_shop_access


class SupplierPaymentAllocationSerializer(serializers.ModelSerializer):
    purchase_number = serializers.ReadOnlyField(source="purchase.purchase_number")

    class Meta:
        model = SupplierPaymentAllocation
        fields = ["id", "purchase", "purchase_number", "allocated_amount"]


class SupplierPaymentSerializer(serializers.ModelSerializer):
    allocations = SupplierPaymentAllocationSerializer(many=True, required=False)
    supplier_name = serializers.ReadOnlyField(source="supplier.name")
    shop_name = serializers.ReadOnlyField(source="shop.name")
    account_name = serializers.ReadOnlyField(source="financial_account.name")
    account_currency_title = serializers.ReadOnlyField(source="financial_account.currency.title")
    account_balance = serializers.SerializerMethodField()
    currency_title = serializers.ReadOnlyField(source="currency.title")
    allocated_amount = serializers.ReadOnlyField()
    unallocated_amount = serializers.ReadOnlyField()

    def get_account_balance(self, obj):
        from shop_management.services.account_controls import account_balance
        return account_balance(obj.financial_account)

    class Meta:
        model = SupplierPayment
        fields = "__all__"
        read_only_fields = ["status", "base_amount", "posted_at", "posted_by", "voided_at", "voided_by", "void_reason", "created_by"]

    def validate(self, attrs):
        shop = attrs.get("shop") or getattr(self.instance, "shop", None)
        request = self.context.get("request")
        if request and shop:
            shop = assert_shop_access(request.user, shop, request)
        supplier = attrs.get("supplier") or getattr(self.instance, "supplier", None)
        account = attrs.get("financial_account") or getattr(self.instance, "financial_account", None)
        currency = attrs.get("currency") or getattr(self.instance, "currency", None)
        service_charge = attrs.get("service_charge", getattr(self.instance, "service_charge", 0)) or 0
        notes = attrs.get("notes", getattr(self.instance, "notes", "")) or ""
        if service_charge > 0 and not str(notes).strip():
            raise serializers.ValidationError({
                "notes": "Explain the bank/Sarafi fee before saving a payment with an additional charge."
            })
        if shop and supplier and supplier.shop_id != shop.pk:
            raise serializers.ValidationError({"supplier": "Supplier must belong to the selected branch."})
        if shop and account and account.shop_id != shop.pk:
            raise serializers.ValidationError({"financial_account": "Account must belong to the selected branch."})
        if shop and currency and currency.shop_id != shop.pk:
            raise serializers.ValidationError({"currency": "Currency must belong to the selected branch."})
        for index, row in enumerate(attrs.get("allocations", [])):
            if shop and row["purchase"].shop_id != shop.pk:
                raise serializers.ValidationError({"allocations": {index: "Purchase must belong to the selected branch."}})
        if self.instance and self.instance.status != SupplierPayment.Status.DRAFT:
            raise serializers.ValidationError("Only draft payments can be edited.")
        return attrs

    def create(self, validated_data):
        allocations = validated_data.pop("allocations", [])
        payment = SupplierPayment.objects.create(created_by=self.context["request"].user, **validated_data)
        for row in allocations:
            SupplierPaymentAllocation.objects.create(payment=payment, created_by=self.context["request"].user, **row)
        return payment

    def update(self, instance, validated_data):
        allocations = validated_data.pop("allocations", None)
        instance = super().update(instance, validated_data)
        if allocations is not None:
            instance.allocations.all().delete()
            for row in allocations:
                SupplierPaymentAllocation.objects.create(payment=instance, created_by=self.context["request"].user, **row)
        return instance
