from django.db.models import Count, IntegerField, OuterRef, Prefetch, Subquery
from django.db.models.functions import Coalesce
from rest_framework import viewsets
from drf_spectacular.utils import extend_schema, OpenApiParameter

from .models import (
    CarBooking, Reimbursement, ReimbursementReceipt, PaymentRequest, DocumentRequest,
    LetterPrefix, LetterNumber, Announcement, SOPCategory, SOP, SOPVersion, InternalGuide,
    HelpdeskTicket
)
from .serializers import (
    CarBookingSerializer, ReimbursementSerializer, ReimbursementReceiptSerializer,
    PaymentRequestSerializer,
    DocumentRequestSerializer, LetterPrefixSerializer, LetterNumberSerializer,
    AnnouncementSerializer, SOPCategorySerializer, SOPSerializer, InternalGuideSerializer,
    HelpdeskTicketSerializer
)

from rest_framework.permissions import IsAuthenticated

from apps.core.permissions import HasMenuPermission, IsOwnerOrFullScope, user_has_full_scope


class OwnRequestScopedMixin:
    """Self-service request lists: users without full administrations scope see
    only their own (`requester`) records; managers see all. Object-level writes
    are guarded by IsOwnerOrFullScope."""
    permission_classes = [IsAuthenticated, HasMenuPermission, IsOwnerOrFullScope]

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user
        if not user_has_full_scope(user, "administrations"):
            qs = qs.filter(requester=user)
        requester_id = self.request.query_params.get("requester")
        if requester_id:
            qs = qs.filter(requester_id=requester_id)
        return qs


class CarBookingViewSet(viewsets.ModelViewSet):
    """ViewSet for CarBooking management."""
    rbac_domain = "administrations"
    permission_classes = [IsAuthenticated, HasMenuPermission, IsOwnerOrFullScope]
    queryset = CarBooking.objects.select_related("requester", "approved_by", "vehicle")
    serializer_class = CarBookingSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user
        # Users without full administrations scope only see their own bookings.
        if not user_has_full_scope(user, "administrations"):
            qs = qs.filter(requester=user)
        requester_id = self.request.query_params.get("requester")
        if requester_id:
            qs = qs.filter(requester_id=requester_id)
        status_param = self.request.query_params.get("status")
        if status_param:
            qs = qs.filter(status=status_param)
        return qs

    def perform_create(self, serializer):
        serializer.save(requester=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requester", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class ReimbursementViewSet(OwnRequestScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Reimbursement management."""
    rbac_domain = "administrations"
    queryset = Reimbursement.objects.select_related("requester", "approved_by").prefetch_related(
        "receipt_files__uploaded_by"
    )
    serializer_class = ReimbursementSerializer

    def perform_create(self, serializer):
        serializer.save(requester=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requester", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class ReimbursementReceiptViewSet(viewsets.ModelViewSet):
    """ViewSet for reimbursement receipt files (image / PDF uploads)."""
    rbac_domain = "administrations"
    queryset = ReimbursementReceipt.objects.select_related("uploaded_by")
    serializer_class = ReimbursementReceiptSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        reimbursement_id = self.request.query_params.get("reimbursement")
        if reimbursement_id:
            qs = qs.filter(reimbursement_id=reimbursement_id)
        return qs

    def perform_create(self, serializer):
        from apps.core.uploads import validate_upload
        upload = self.request.FILES.get("file")
        if upload is not None:
            validate_upload(upload)
        serializer.save(uploaded_by=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="reimbursement", type=str, description="Filter by reimbursement ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class PaymentRequestViewSet(OwnRequestScopedMixin, viewsets.ModelViewSet):
    """ViewSet for PaymentRequest management."""
    rbac_domain = "administrations"
    queryset = PaymentRequest.objects.select_related("requester", "approved_by", "source_invoice")
    serializer_class = PaymentRequestSerializer

    def perform_create(self, serializer):
        serializer.save(requester=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requester", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class DocumentRequestViewSet(OwnRequestScopedMixin, viewsets.ModelViewSet):
    """ViewSet for DocumentRequest management."""
    rbac_domain = "administrations"
    # full_number reads letter.prefix.code — select it inside the prefetch or
    # each serialized row re-queries the prefix.
    queryset = DocumentRequest.objects.select_related("requester").prefetch_related(
        Prefetch("letters", queryset=LetterNumber.objects.select_related("prefix"))
    )
    serializer_class = DocumentRequestSerializer

    def perform_create(self, serializer):
        serializer.save(requester=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requester", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class LetterPrefixViewSet(viewsets.ModelViewSet):
    """ViewSet for LetterPrefix (department/unit letter types). Managed by secretariat."""
    rbac_domain = "administrations"
    # letter_count_anno feeds LetterPrefixSerializer.get_letter_count (avoids per-row count()).
    queryset = LetterPrefix.objects.annotate(letter_count_anno=Count("letters"))
    serializer_class = LetterPrefixSerializer


class LetterNumberViewSet(viewsets.ModelViewSet):
    """ViewSet for LetterNumber management."""
    rbac_domain = "administrations"
    queryset = LetterNumber.objects.select_related("prefix", "document_request")
    serializer_class = LetterNumberSerializer

    def perform_create(self, serializer):
        from django.db.models import Max
        prefix = serializer.validated_data["prefix"]
        year = serializer.validated_data["year"]
        # Sequence continues per prefix within a year, resets each new year.
        last = (
            LetterNumber.objects
            .filter(prefix=prefix, year=year)
            .aggregate(m=Max("sequence"))["m"]
        )
        letter = serializer.save(sequence=(last or 0) + 1)
        # Issuing a letter for a request marks that request fulfilled.
        if letter.document_request_id:
            req = letter.document_request
            if req.status != "ready":
                req.status = "ready"
                req.save(update_fields=["status"])

    @extend_schema(
        parameters=[
            OpenApiParameter(name="prefix", type=str, description="Filter by prefix"),
            OpenApiParameter(name="year", type=int, description="Filter by year"),
            OpenApiParameter(name="month", type=int, description="Filter by month"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AnnouncementViewSet(viewsets.ModelViewSet):
    """ViewSet for Announcement management."""
    rbac_domain = "administrations"
    queryset = Announcement.objects.select_related("author")
    serializer_class = AnnouncementSerializer


class SOPCategoryViewSet(viewsets.ModelViewSet):
    """ViewSet for SOPCategory (managed SOP category list)."""
    rbac_domain = "administrations"
    # SOP.category stores the category *name* (no FK), so count via subquery.
    # sop_count_anno feeds SOPCategorySerializer.get_sop_count (avoids per-row count()).
    queryset = SOPCategory.objects.annotate(
        sop_count_anno=Coalesce(
            Subquery(
                SOP.objects.filter(category=OuterRef("name"))
                .order_by().values("category")
                .annotate(c=Count("id")).values("c"),
                output_field=IntegerField(),
            ),
            0,
        )
    )
    serializer_class = SOPCategorySerializer


class SOPViewSet(viewsets.ModelViewSet):
    """ViewSet for SOP management. HR authors SOPs; all staff can read them."""
    rbac_domain = "administrations"
    queryset = SOP.objects.select_related("author").prefetch_related("versions__changed_by")
    serializer_class = SOPSerializer

    @extend_schema(
        parameters=[
            OpenApiParameter(name="category", type=str, description="Filter by category"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    def perform_create(self, serializer):
        change_note = serializer.validated_data.pop("change_note", "")
        user = self.request.user if self.request.user.is_authenticated else None
        sop = serializer.save(author=user)
        SOPVersion.objects.create(
            sop=sop, version=sop.version, content=sop.content,
            effective_date=sop.effective_date,
            change_note=change_note or "Initial version", changed_by=user,
        )

    def perform_update(self, serializer):
        change_note = serializer.validated_data.pop("change_note", "")
        old_version = serializer.instance.version
        sop = serializer.save()
        if sop.version != old_version:
            SOPVersion.objects.create(
                sop=sop, version=sop.version, content=sop.content,
                effective_date=sop.effective_date,
                change_note=change_note or f"Updated to {sop.version}",
                changed_by=self.request.user if self.request.user.is_authenticated else None,
            )


class HelpdeskTicketViewSet(OwnRequestScopedMixin, viewsets.ModelViewSet):
    """ViewSet for HelpdeskTicket management. Users see their own tickets;
    managers with full administrations scope see all."""
    rbac_domain = "administrations"
    queryset = HelpdeskTicket.objects.select_related("requester", "assigned_to")
    serializer_class = HelpdeskTicketSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        status_param = self.request.query_params.get("status")
        if status_param:
            qs = qs.filter(status=status_param)
        return qs

    def perform_create(self, serializer):
        serializer.save(requester=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requester", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class InternalGuideViewSet(viewsets.ModelViewSet):
    """ViewSet for InternalGuide management."""
    rbac_domain = "administrations"
    queryset = InternalGuide.objects.select_related("author")
    serializer_class = InternalGuideSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="category", type=str, description="Filter by category"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
