from django.core.exceptions import ValidationError as DjangoValidationError
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.filters import SearchFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from account.permissions import HasTabPermission, HasActionPermission, get_user_data_filter, user_can_see_all_shop_data
from shop_management.models.data.accounting import JournalEntry
from shop_management.serializers.data.journal import JournalEntrySerializer
from shop_management.services.journals import post_journal, void_journal
from shop_management.views.data.pagination import CustomPagination


class JournalEntryViewSet(viewsets.ModelViewSet):
    serializer_class = JournalEntrySerializer
    permission_classes = [IsAuthenticated, HasTabPermission, HasActionPermission]
    action_permission_map = {"create": "accounting.create_manual_journal", "post": "accounting.post_journal", "void": "accounting.void_journal"}
    required_tab = "accounts"
    pagination_class = CustomPagination
    filter_backends = [DjangoFilterBackend, SearchFilter]
    filterset_fields = ["shop", "status", "source_type", "entry_date"]
    search_fields = ["journal_number", "description", "source_type", "source_id"]

    def get_queryset(self):
        queryset = JournalEntry.objects.select_related("shop", "posted_by", "voided_by").prefetch_related("lines__ledger_account", "lines__transaction_currency")
        if not user_can_see_all_shop_data(self.request.user, self.required_tab):
            queryset = queryset.filter(get_user_data_filter(self.request.user, "shop"))
        return queryset.order_by("-entry_date", "-id")

    def destroy(self, request, *args, **kwargs):
        if self.get_object().status != JournalEntry.Status.DRAFT:
            return Response({"detail": "Only draft journal entries can be deleted."}, status=status.HTTP_400_BAD_REQUEST)
        return super().destroy(request, *args, **kwargs)

    def _run(self, request, operation):
        try:
            result = operation(self.get_object().pk)
        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(request, lambda entry_id: post_journal(entry_id, user=request.user))

    @action(detail=True, methods=["post"])
    def void(self, request, pk=None):
        return self._run(request, lambda entry_id: void_journal(entry_id, user=request.user, reason=request.data.get("reason", "")))
