from django.db import transaction
from rest_framework import serializers

from shop_management.models.data.stock_adjustment import StockAdjustment, StockAdjustmentItem
from shop_management.serializers.data.base import DataRootSerializer
from account.permissions import assert_warehouse_access


class StockAdjustmentItemSerializer(DataRootSerializer):
    product_details = serializers.SerializerMethodField()
    batch_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()

    class Meta:
        model = StockAdjustmentItem
        exclude = ("adjustment",)
        read_only_fields = ("created_by",)

    def get_product_details(self, obj):
        return {"id": obj.product_id, "name": obj.product.name, "code": obj.product.code}

    def get_batch_details(self, obj):
        return {"id": obj.batch_id, "batch_number": obj.batch.batch_number, "expiry_date": obj.batch.expiry_date}

    def get_currency_details(self, obj):
        return {"id": obj.currency_id, "title": obj.currency.title}

    def validate(self, attrs):
        product = attrs.get("product") or getattr(self.instance, "product", None)
        batch = attrs.get("batch") or getattr(self.instance, "batch", None)
        currency = attrs.get("currency") or getattr(self.instance, "currency", None)
        direction = attrs.get("direction") or getattr(self.instance, "direction", None)
        unit_cost = attrs.get("unit_cost", getattr(self.instance, "unit_cost", None))
        errors = {}
        if product and batch and batch.product_id != product.id:
            errors["batch"] = "Batch must belong to the selected product."
        if product and currency and currency.shop_id != product.shop_id:
            errors["currency"] = "Currency must belong to the product branch."
        if direction == StockAdjustmentItem.Direction.IN and unit_cost is None:
            errors["unit_cost"] = "Unit cost is required for incoming adjustments."
        if errors:
            raise serializers.ValidationError(errors)
        return attrs


class StockAdjustmentSerializer(DataRootSerializer):
    items = StockAdjustmentItemSerializer(many=True)
    warehouse_details = serializers.SerializerMethodField()
    status_display = serializers.CharField(source="get_status_display", read_only=True)
    posted_by_name = serializers.CharField(source="posted_by.username", read_only=True)
    voided_by_name = serializers.CharField(source="voided_by.username", read_only=True)

    class Meta:
        model = StockAdjustment
        fields = "__all__"
        read_only_fields = (
            "adjustment_number", "status", "posted_at", "posted_by",
            "voided_at", "voided_by", "void_reason", "created_by",
            "adjustment_kind",
        )

    def get_warehouse_details(self, obj):
        return {"id": obj.warehouse_id, "code": obj.warehouse.code, "name": obj.warehouse.name,
                "shop": obj.warehouse.shop_id, "shop_name": obj.warehouse.shop.name}

    def validate(self, attrs):
        warehouse = attrs.get("warehouse") or getattr(self.instance, "warehouse", None)
        request = self.context.get("request")
        if request and warehouse:
            warehouse = assert_warehouse_access(request.user, warehouse, request, active_only=True)
        if self.instance and self.instance.status != StockAdjustment.Status.DRAFT:
            raise serializers.ValidationError("Posted or void adjustments cannot be edited.")
        items = attrs.get("items")
        if items is not None:
            for index, item in enumerate(items):
                product = item["product"]
                if warehouse and product.shop_id != warehouse.shop_id:
                    raise serializers.ValidationError(
                        {"items": {index: "Product must belong to the warehouse branch."}}
                    )
        return attrs

    @transaction.atomic
    def create(self, validated_data):
        items = validated_data.pop("items", [])
        request = self.context.get("request")
        adjustment = StockAdjustment.objects.create(
            **validated_data, created_by=request.user if request else None
        )
        for item in items:
            StockAdjustmentItem.objects.create(
                adjustment=adjustment, **item,
                created_by=request.user if request else None,
            )
        return adjustment

    @transaction.atomic
    def update(self, instance, validated_data):
        items = validated_data.pop("items", None)
        instance = super().update(instance, validated_data)
        if items is not None:
            instance.items.all().delete()
            request = self.context.get("request")
            for item in items:
                StockAdjustmentItem.objects.create(
                    adjustment=instance, **item,
                    created_by=request.user if request else None,
                )
        return instance
