from django.core.exceptions import ValidationError as DjangoValidationError
from django.db.models import Q
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from account.permissions import HasTabPermission, HasActionPermission, accessible_shops, assert_warehouse_access
from shop_management.models.data.warehouse_transfer import WarehouseTransfer
from shop_management.models.data.inventory import StockBalance
from shop_management.models.data.shop import Shop
from shop_management.serializers.data.inventory import StockBalanceSerializer
from shop_management.serializers.data.warehouse_transfer import WarehouseTransferSerializer
from shop_management.services.warehouse_transfers import dispatch_transfer, receive_transfer, void_transfer
from shop_management.views.data.base import DataRootViewSet


class WarehouseTransferViewSet(DataRootViewSet):
    serializer_class = WarehouseTransferSerializer
    permission_classes = [IsAuthenticated, HasTabPermission, HasActionPermission]
    action_permission_map = {"create": "inventory.transfer_stock", "dispatch_transfer_action": "inventory.transfer_stock", "receive": "inventory.transfer_stock", "void": "inventory.transfer_stock"}
    required_tab = "stock_transfer"

    def get_queryset(self):
        queryset = WarehouseTransfer.objects.select_related("shop", "source_warehouse", "destination_warehouse").prefetch_related("items__product", "items__batch", "items__cost_currency")
        queryset = queryset.filter(shop__in=accessible_shops(self.request.user, self.request)).distinct()
        if self.request.query_params.get("shop"):
            queryset = queryset.filter(shop_id=self.request.query_params["shop"])
        return queryset.order_by("-id")

    def update(self, request, *args, **kwargs):
        if self.get_object().status != WarehouseTransfer.Status.DRAFT:
            raise ValidationError("Only draft transfers can be edited.")
        return super().update(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.status != WarehouseTransfer.Status.DRAFT:
            raise ValidationError("Only draft transfers can be deleted.")
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def _run(self, service, **kwargs):
        try:
            result = service(self.get_object().pk, user=self.request.user, **kwargs)
        except DjangoValidationError as exc:
            raise ValidationError(exc.message_dict if hasattr(exc, "message_dict") else exc.messages)
        return Response(self.get_serializer(result).data)

    @action(detail=True, methods=["post"], url_path="dispatch")
    def dispatch_transfer_action(self, request, pk=None):
        return self._run(dispatch_transfer)

    @action(detail=True, methods=["post"])
    def receive(self, request, pk=None):
        return self._run(receive_transfer)

    @action(detail=True, methods=["post"])
    def void(self, request, pk=None):
        return self._run(void_transfer, reason=request.data.get("reason", ""))

    @action(detail=False, methods=["get"], url_path="available-stock")
    def available_stock(self, request):
        warehouse_id = request.query_params.get("warehouse")
        if not warehouse_id:
            raise ValidationError({"warehouse": "Warehouse is required."})
        warehouse = assert_warehouse_access(request.user, warehouse_id, request, active_only=True)
        balances = StockBalance.objects.filter(
            warehouse=warehouse, quantity__gt=0
        ).select_related("warehouse", "product", "batch", "cost_currency")
        return Response(StockBalanceSerializer(balances, many=True).data)
