from django.db.models import Q
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.exceptions import ValidationError
from django.core.exceptions import ValidationError as DjangoValidationError

from account.permissions import HasTabPermission, HasActionPermission, user_can_see_all_shop_data
from shop_management.models.data.account_transaction import AccountTransaction, AccountTransactionType
from shop_management.models.data.account_transfer import AccountTransfer
from shop_management.serializers.data.account_transfer import AccountTransferSerializer
from shop_management.views.data.base import DataRootViewSet
from shop_management.views.data.pagination import CustomPagination
from shop_management.services.finance_transfers import post_finance_transfer, void_finance_transfer


class AccountTransferViewSet(DataRootViewSet):
    serializer_class = AccountTransferSerializer
    permission_classes = [IsAuthenticated, HasTabPermission, HasActionPermission]
    action_permission_map = {"create": "finance.transfer_accounts", "post": "finance.transfer_accounts", "void": "finance.transfer_accounts"}
    required_tab = "account_transfers"
    filter_backends = [DjangoFilterBackend, SearchFilter]
    filterset_fields = [
        "from_account",
        "to_account",
        "from_currency",
        "to_currency",
        "transfer_date",
        "uses_controlled_engine",
        "status",
    ]
    search_fields = ["reference", "note", "from_account__name", "to_account__name"]
    pagination_class = CustomPagination

    def get_queryset(self):
        qs = AccountTransfer.objects.select_related(
            "from_account",
            "to_account",
            "from_account__shop",
            "from_currency",
            "to_currency",
        ).order_by("-transfer_date", "-id")
        if user_can_see_all_shop_data(self.request.user, self.required_tab):
            return qs
        return qs.filter(
            Q(from_account__shop__owner=self.request.user)
            | Q(from_account__shop__assigned_users=self.request.user)
        ).distinct()

    def perform_destroy(self, instance):
        if instance.uses_controlled_engine:
            if instance.status != AccountTransfer.Status.DRAFT:
                raise ValidationError("Posted or cancelled controlled transfers cannot be deleted.")
            return super().perform_destroy(instance)
        serializer = self.get_serializer()
        out_description = serializer._txn_description(instance, "out")
        in_description = serializer._txn_description(instance, "in")
        AccountTransaction.objects.filter(
            account=instance.from_account,
            transaction_type=AccountTransactionType.DEBIT,
            amount=instance.amount,
            transaction_date=instance.transfer_date,
            description=out_description,
        ).delete()
        AccountTransaction.objects.filter(
            account=instance.to_account,
            transaction_type=AccountTransactionType.CREDIT,
            amount=instance.amount,
            transaction_date=instance.transfer_date,
            description=in_description,
        ).delete()
        super().perform_destroy(instance)

    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"])
    def post(self, request, pk=None):
        return self._run(post_finance_transfer)

    @action(detail=True, methods=["post"])
    def void(self, request, pk=None):
        return self._run(void_finance_transfer, reason=request.data.get("reason", ""))
