from rest_framework import serializers
from django.db import models
from shop_management.serializers.data.base import DataRootSerializer
from shop_management.models.data.sale import Sale, SaleItem, SaleItemBatchAllocation
from shop_management.serializers.data.organization import branch_profile_details


class SaleItemBatchAllocationSerializer(serializers.ModelSerializer):
    batch_number = serializers.ReadOnlyField(source="batch.batch_number")

    class Meta:
        model = SaleItemBatchAllocation
        fields = ["id", "batch", "batch_number", "quantity", "unit_cost"]
        read_only_fields = ["unit_cost"]

class SaleItemSerializer(DataRootSerializer):
    warehouse_details = serializers.SerializerMethodField()
    batch_allocations = SaleItemBatchAllocationSerializer(many=True, read_only=True)
    product_details = serializers.SerializerMethodField()
    unit_details = serializers.SerializerMethodField()
    purchase_details = serializers.SerializerMethodField()
    unit_cost = serializers.SerializerMethodField()
    wholesale_price = serializers.SerializerMethodField()
    retail_price = serializers.SerializerMethodField()
    line_cost = serializers.SerializerMethodField()
    line_total = serializers.SerializerMethodField()
    line_profit = serializers.SerializerMethodField()

    class Meta:
        model = SaleItem
        fields = '__all__'

    def get_warehouse_details(self, obj):
        warehouse = obj.warehouse or getattr(obj.sale, "warehouse", None)
        if not warehouse:
            return None
        return {"id": warehouse.id, "code": warehouse.code, "name": warehouse.name}

    def get_purchase_details(self, obj):
        if obj.purchase_item:
            pi = obj.purchase_item
            p = pi.purchase
            return {
                "id": p.id,
                "purchase_date": p.purchase_date,
                "supplier_name": p.supplier.name if p.supplier else "N/A",
                "buy_price": str(pi.buy_price),
                "wholesale_price": str(pi.wholesale_price) if pi.wholesale_price is not None else None,
                "retail_price": str(pi.retail_price) if pi.retail_price is not None else None,
                "sale_price": str(pi.sale_price) if pi.sale_price is not None else None,
            }
        return None

    def get_product_details(self, obj):
        if obj.product:
            # Get current stock quantity and prices for this product in the sale's shop
            from shop_management.models.data.stock import Stock
            stock = Stock.objects.filter(
                shop=obj.sale.shop,
                purchase_item__product=obj.product
            ).first()
            stock_quantity = Stock.objects.filter(
                shop=obj.sale.shop,
                purchase_item__product=obj.product
            ).aggregate(total=models.Sum('quantity'))['total'] or 0
            
            return {
                "id": obj.product.id,
                "name": obj.product.name,
                "code": obj.product.code,
                "stock_quantity": stock_quantity,
                "buy_price": str(stock.average_buy_price if stock and getattr(stock, "average_buy_price", None) is not None else (stock.purchase_item.buy_price if stock and stock.purchase_item else None)),
                "wholesale_price": str(stock.average_wholesale if stock and getattr(stock, "average_wholesale", None) is not None else (stock.purchase_item.wholesale_price if stock and stock.purchase_item else None)) if stock and (getattr(stock, "average_wholesale", None) is not None or (stock.purchase_item and stock.purchase_item.wholesale_price is not None)) else None,
                "retail_price": str(stock.average_retail if stock and getattr(stock, "average_retail", None) is not None else (stock.purchase_item.retail_price if stock and stock.purchase_item else None)) if stock and (getattr(stock, "average_retail", None) is not None or (stock.purchase_item and stock.purchase_item.retail_price is not None)) else None,
                "sale_price": str(stock.sale_price) if stock else None,
            }
        return None

    def get_unit_details(self, obj):
        if obj.unit:
            return {"id": obj.unit.id, "title": obj.unit.title}
        return None

    def _unit_cost(self, obj):
        if obj.buy_price is not None:
            return obj.buy_price
        if obj.purchase_item:
            stock = None
            try:
                from shop_management.models.data.stock import Stock
                stock = Stock.objects.filter(
                    shop=obj.sale.shop,
                    purchase_item__product=obj.product
                ).first()
            except Exception:
                stock = None
            if stock and getattr(stock, "average_buy_price", None) is not None:
                return stock.average_buy_price
            if obj.purchase_item.buy_price is not None:
                return obj.purchase_item.buy_price
        return 0

    def get_unit_cost(self, obj):
        return str(self._unit_cost(obj))

    def get_wholesale_price(self, obj):
        if obj.purchase_item and obj.purchase_item.wholesale_price is not None:
            return str(obj.purchase_item.wholesale_price)
        return obj.product_details.get("wholesale_price") if getattr(obj, "product_details", None) else None

    def get_retail_price(self, obj):
        if obj.purchase_item and obj.purchase_item.retail_price is not None:
            return str(obj.purchase_item.retail_price)
        return obj.product_details.get("retail_price") if getattr(obj, "product_details", None) else None

    def get_line_cost(self, obj):
        return str((obj.quantity or 0) * self._unit_cost(obj))

    def get_line_total(self, obj):
        return str((obj.quantity or 0) * (obj.sale_price or 0))

    def get_line_profit(self, obj):
        return str(((obj.sale_price or 0) - self._unit_cost(obj)) * (obj.quantity or 0))


class SaleSerializer(DataRootSerializer):
    shop_details = serializers.SerializerMethodField()
    customer_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()
    received_amount = serializers.DecimalField(max_digits=10, decimal_places=2)
    total_amount = serializers.SerializerMethodField()
    effective_total_amount = serializers.SerializerMethodField()
    total_paid = serializers.SerializerMethodField()
    total_remained_amount = serializers.SerializerMethodField()
    auto_payment_details = serializers.SerializerMethodField()
    sale_items = SaleItemSerializer(many=True, read_only=True)
    warehouse_details = serializers.SerializerMethodField()
    document_status_display = serializers.CharField(source="get_document_status_display", read_only=True)

    class Meta:
        model = Sale
        fields = '__all__'
        read_only_fields = ["previous_balance", "previous_balance_details", "replaces_sale"]

    def get_shop_details(self, obj):
        if obj.shop:
            return {
                "id": obj.shop.id,
                "name": obj.shop.name,
                "profile": branch_profile_details(obj.shop),
            }
        return None

    def get_customer_details(self, obj):
        if obj.customer:
            c = obj.customer
            base_name = (c.name or "").strip()
            if not base_name and c.email:
                base_name = str(c.email).split("@")[0].strip()
            if not base_name and c.contact:
                base_name = str(c.contact).strip()
            if not base_name:
                base_name = (obj.general_customer_name or "").strip()
            if not base_name:
                base_name = "General Customer" if getattr(c, "is_general_customer", False) else "Unnamed Customer"
            customer_name = (
                f"General Customer\n{base_name}"
                if getattr(c, "is_general_customer", False)
                else base_name
            )
            return {
                "id": c.id,
                "name": customer_name,
                "contact": c.contact,
                "address": c.address,
                "email": c.email,
                "total_owed_by_currency": c.get_total_owed_by_currency(),
                "credit_by_currency": c.get_credit_by_currency(),
            }
        if obj.general_customer_name or obj.general_customer_phone:
            return {
                "id": "general-customer",
                "name": f"General Customer\n{obj.general_customer_name}" if obj.general_customer_name else "General Customer-",
                "contact": obj.general_customer_phone or "",
                "address": "",
                "email": "",
                "is_general_customer": True,
            }
        return {
            "id": "general-customer",
            "name": "General Customer",
            "contact": "",
            "address": "",
            "email": "",
            "is_general_customer": True,
        }

    def get_currency_details(self, obj):
        if obj.currency:
            return {"id": obj.currency.id, "title": obj.currency.title}
        return None

    def get_warehouse_details(self, obj):
        if not obj.warehouse:
            return None
        return {"id": obj.warehouse_id, "code": obj.warehouse.code, "name": obj.warehouse.name}

    def get_total_amount(self, obj):
        return obj.total_amount

    def get_effective_total_amount(self, obj):
        return obj.effective_total_amount

    def get_total_paid(self, obj):
        return obj.total_paid

    def get_total_remained_amount(self, obj):
        return obj.total_remained_amount

    def get_auto_payment_details(self, obj):
        if not obj.customer_id:
            return None
        from shop_management.models.data.customer_balance_payment import CustomerBalancePayment

        payment = (
            CustomerBalancePayment.objects.filter(
                customer_id=obj.customer_id,
                notes__startswith=f"Automatic sale payment for sale #{obj.id}",
            )
            .select_related("account", "account__currency")
            .order_by("-id")
            .first()
        )
        if not payment:
            return None
        prefix = f"Automatic sale payment for sale #{obj.id}"
        notes = payment.notes or ""
        if notes.startswith(prefix):
            notes = notes[len(prefix):].lstrip(" |")
        return {
            "id": payment.id,
            "account": payment.account_id,
            "account_name": payment.account.name if payment.account else None,
            "account_currency_title": payment.account.currency.title if payment.account and payment.account.currency else None,
            "exchange_rate": payment.exchange_rate,
            "converted_amount": payment.converted_amount,
            "payment_date": payment.payment_date,
            "notes": notes,
        }
