from __future__ import annotations

from django.db.models import Q
from rest_framework import serializers, status
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.access.drf import TenantPermission
from apps.invoicing import services
from apps.invoicing.models import Invoice, InvoiceItem
from interfaces.api_v1.serializers import UserBriefSerializer


class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = InvoiceItem
        fields = ["id", "description", "quantity", "unit_price", "line_total"]


class InvoiceSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True, read_only=True)
    customer = serializers.SerializerMethodField()
    created_by = UserBriefSerializer(read_only=True)
    organization_name = serializers.CharField(source="organization.name", read_only=True)

    class Meta:
        model = Invoice
        fields = [
            "id", "number", "status", "issue_date", "due_date", "notes", "tax_percent", "discount",
            "subtotal", "tax", "total", "customer", "created_by", "issued_at", "paid_at", "cancelled_at",
            "created_at", "items", "organization_name",
        ]

    def get_customer(self, obj):
        c = obj.customer
        return {"id": c.pk, "name": c.name, "phone": c.phone, "email": c.email, "address": c.address}


class ItemInput(serializers.Serializer):
    description = serializers.CharField(max_length=300)
    quantity = serializers.DecimalField(max_digits=12, decimal_places=3)
    unit_price = serializers.IntegerField(min_value=0)


class InvoiceInput(serializers.Serializer):
    customer = serializers.IntegerField(required=False)
    issue_date = serializers.DateField(required=False)
    due_date = serializers.DateField(required=False, allow_null=True)
    notes = serializers.CharField(required=False, allow_blank=True)
    tax_percent = serializers.DecimalField(max_digits=5, decimal_places=2, required=False)
    discount = serializers.IntegerField(required=False, min_value=0)
    items = ItemInput(many=True, required=False)


class Pagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = "page_size"
    max_page_size = 100


class InvoiceListCreateView(APIView):
    permission_classes = [TenantPermission]
    module_code = "invoicing"
    permission_map = {"GET": "invoicing.view", "POST": "invoicing.create"}

    def get(self, request):
        qs = services.visible_invoices(request.tenant, "invoicing.view")
        params = request.query_params
        if params.get("status"):
            qs = qs.filter(status=params["status"])
        if params.get("customer"):
            qs = qs.filter(customer_id=params["customer"])
        search = (params.get("search") or "").strip()
        if search:
            q = Q(customer__name__icontains=search)
            if search.isdigit():
                q |= Q(number=int(search))
            qs = qs.filter(q)
        paginator = Pagination()
        page = paginator.paginate_queryset(qs, request)
        return paginator.get_paginated_response(InvoiceSerializer(page, many=True).data)

    def post(self, request):
        data = InvoiceInput(data=request.data)
        data.is_valid(raise_exception=True)
        if "issue_date" not in data.validated_data:
            raise serializers.ValidationError({"issue_date": "This field is required."})
        invoice = services.create_invoice(request.tenant, data=data.validated_data, request=request)
        return Response(InvoiceSerializer(invoice).data, status=status.HTTP_201_CREATED)


class InvoiceDetailView(APIView):
    permission_classes = [TenantPermission]
    module_code = "invoicing"
    permission_map = {"GET": "invoicing.view", "PATCH": "invoicing.update", "DELETE": "invoicing.delete"}

    def get(self, request, pk):
        return Response(InvoiceSerializer(services.get_invoice(request.tenant, pk, "invoicing.view")).data)

    def patch(self, request, pk):
        invoice = services.get_invoice(request.tenant, pk, "invoicing.update")
        data = InvoiceInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        invoice = services.update_invoice(request.tenant, invoice, data=data.validated_data, request=request)
        return Response(InvoiceSerializer(invoice).data)

    def delete(self, request, pk):
        services.delete_invoice(request.tenant, services.get_invoice(request.tenant, pk, "invoicing.delete"), request=request)
        return Response(status=status.HTTP_204_NO_CONTENT)


class InvoiceActionView(APIView):
    """issue / pay / cancel; each needs its own permission (set via as_view(action=..., permission=...))."""

    permission_classes = [TenantPermission]
    module_code = "invoicing"
    action = "issue"
    scope_map = {"POST": "organization"}

    @property
    def permission_map(self):
        return {"POST": {"issue": "invoicing.issue", "pay": "invoicing.settle", "cancel": "invoicing.cancel"}[self.action]}

    def post(self, request, pk):
        permission = self.permission_map["POST"]
        invoice = services.get_invoice(request.tenant, pk, permission)
        fn = {"issue": services.issue_invoice, "pay": services.mark_paid, "cancel": services.cancel_invoice}[self.action]
        return Response(InvoiceSerializer(fn(request.tenant, invoice, request=request)).data)


class InvoiceSummaryView(APIView):
    permission_classes = [TenantPermission]
    module_code = "invoicing"
    permission_map = {"GET": "invoicing.view"}

    def get(self, request):
        return Response(services.summary(request.tenant))


class CustomerChoicesView(APIView):
    """Customers the caller can bill, for the invoice form."""

    permission_classes = [TenantPermission]
    module_code = "invoicing"
    permission_map = {"GET": "invoicing.create"}

    def get(self, request):
        qs = services.usable_customers(request.tenant)
        search = (request.query_params.get("search") or "").strip()
        if search:
            qs = qs.filter(name__icontains=search)
        return Response([{"id": c.pk, "name": c.name} for c in qs.order_by("name")[:100]])
