from django.db.models import Count
from rest_framework import viewsets
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter

from .models import Currency, TaxRate, BankAccount, BudgetCategory, BudgetItem, Invoice, to_letters
from .serializers import (
    CurrencySerializer, TaxRateSerializer, BankAccountSerializer,
    BudgetCategorySerializer, BudgetItemSerializer, InvoiceSerializer,
)


class CurrencyViewSet(viewsets.ModelViewSet):
    """Org-wide currencies used in finance forms."""
    rbac_domain = "finance"
    queryset = Currency.objects.all()
    serializer_class = CurrencySerializer


class BankAccountViewSet(viewsets.ModelViewSet):
    """Org bank accounts for receiving money. Filter by currency."""
    rbac_domain = "finance"
    serializer_class = BankAccountSerializer

    def get_queryset(self):
        qs = BankAccount.objects.all()
        currency = self.request.query_params.get("currency")
        if currency:
            qs = qs.filter(currency=currency)
        return qs

    def perform_create(self, serializer):
        obj = serializer.save()
        self._enforce_single_default(obj)

    def perform_update(self, serializer):
        obj = serializer.save()
        self._enforce_single_default(obj)

    @staticmethod
    def _enforce_single_default(obj):
        # One default per currency.
        if obj.is_default:
            BankAccount.objects.exclude(pk=obj.pk).filter(
                currency=obj.currency, is_default=True,
            ).update(is_default=False)


class TaxRateViewSet(viewsets.ModelViewSet):
    """Org-wide tax rates (percentages) used on invoices."""
    rbac_domain = "finance"
    queryset = TaxRate.objects.all()
    serializer_class = TaxRateSerializer

    def perform_create(self, serializer):
        obj = serializer.save()
        self._enforce_single_default(obj)

    def perform_update(self, serializer):
        obj = serializer.save()
        self._enforce_single_default(obj)

    @staticmethod
    def _enforce_single_default(obj):
        # Only one rate may be the default; unset the others.
        if obj.is_default:
            TaxRate.objects.exclude(pk=obj.pk).filter(is_default=True).update(is_default=False)


class BudgetCategoryViewSet(viewsets.ModelViewSet):
    """Org-wide budget categories."""
    rbac_domain = "finance"
    # item_count_anno feeds the serializer; counting in SQL beats prefetching
    # every item row just to len() it.
    queryset = BudgetCategory.objects.annotate(item_count_anno=Count("items"))
    serializer_class = BudgetCategorySerializer

    def perform_create(self, serializer):
        # Auto-assign the next free letter code (A, B, C, …).
        n = BudgetCategory.objects.count()
        while True:
            n += 1
            code = to_letters(n)
            if not BudgetCategory.objects.filter(code=code).exists():
                break
        serializer.save(code=code)


class BudgetItemViewSet(viewsets.ModelViewSet):
    """Budget line items, optionally filtered by category."""
    rbac_domain = "finance"
    queryset = BudgetItem.objects.select_related("category").all()
    serializer_class = BudgetItemSerializer

    def perform_create(self, serializer):
        # Code is <category-code>.<n> at the top level (B.1, B.2), or
        # <parent-code>.<n> when nested under a parent item (B.1.1, B.1.2).
        category = serializer.validated_data["category"]
        parent = serializer.validated_data.get("parent")
        if parent is not None:
            prefix = parent.code
            siblings = parent.children.count()
        else:
            prefix = category.code
            siblings = category.items.filter(parent__isnull=True).count()
        n = siblings
        while True:
            n += 1
            code = f"{prefix}.{n}"
            if not BudgetItem.objects.filter(category=category, code=code).exists():
                break
        serializer.save(code=code)

    @extend_schema(
        parameters=[OpenApiParameter(name="category", type=str, description="Filter by category ID")]
    )
    def list(self, request, *args, **kwargs):
        qs = self.get_queryset()
        category = request.query_params.get("category")
        if category:
            qs = qs.filter(category_id=category)
        serializer = self.get_serializer(qs, many=True)
        return Response(serializer.data)


class InvoiceViewSet(viewsets.ModelViewSet):
    """Receivable invoices we issue to donors. Filter by status/donor/project."""
    rbac_domain = "finance"
    serializer_class = InvoiceSerializer

    def get_queryset(self):
        qs = (
            Invoice.objects.select_related("donor", "project", "event")
            .prefetch_related("lines")
        )
        params = self.request.query_params
        for field in ("status", "donor", "project", "event"):
            val = params.get(field)
            if val:
                qs = qs.filter(**{field if field == "status" else f"{field}_id": val})
        return qs

    def perform_create(self, serializer):
        # Auto-assign a sequential invoice number: INV-YYYY-NNNN.
        from django.utils import timezone
        year = timezone.now().year
        prefix = f"INV-{year}-"
        last = (
            Invoice.objects.filter(invoice_number__startswith=prefix)
            .order_by("-invoice_number")
            .values_list("invoice_number", flat=True)
            .first()
        )
        seq = (int(last.rsplit("-", 1)[-1]) + 1) if last else 1
        serializer.save(invoice_number=f"{prefix}{seq:04d}")
