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.finance import services
from apps.finance.models import FinanceCategory, Transaction

ORG = {m: "organization" for m in ("GET", "POST", "PATCH", "PUT", "DELETE")}


class Base(APIView):
    """Finance is organization-level only: every method needs organization scope."""

    permission_classes = [TenantPermission]
    module_code = "finance"
    scope_map = ORG


class AccountInput(serializers.Serializer):
    name = serializers.CharField(max_length=120, required=False)
    type = serializers.CharField(required=False)
    opening_balance = serializers.IntegerField(required=False)
    is_active = serializers.BooleanField(required=False)


class CategoryInput(serializers.Serializer):
    name = serializers.CharField(max_length=120)
    kind = serializers.CharField()


class TransactionInput(serializers.Serializer):
    kind = serializers.CharField(required=False)
    amount = serializers.IntegerField(required=False)
    account = serializers.IntegerField(required=False)
    category = serializers.IntegerField(required=False, allow_null=True)
    date = serializers.DateField(required=False)
    description = serializers.CharField(max_length=300, required=False, allow_blank=True)


class RecordInput(serializers.Serializer):
    source = serializers.ChoiceField(choices=["invoice", "payroll"])
    source_id = serializers.IntegerField()
    account = serializers.IntegerField()


def _transaction(t: Transaction) -> dict:
    return {
        "id": t.pk, "kind": t.kind, "amount": t.amount, "date": t.date.isoformat(), "description": t.description,
        "account": {"id": t.account_id, "name": t.account.name},
        "category": {"id": t.category_id, "name": t.category.name} if t.category_id else None,
        "source_type": t.source_type, "source_id": t.source_id,
    }


def _account(a, balance) -> dict:
    return {"id": a.pk, "name": a.name, "type": a.type, "opening_balance": a.opening_balance, "is_active": a.is_active, "balance": balance}


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


class AccountListCreateView(Base):
    permission_map = {"GET": "finance.view", "POST": "finance.manage"}

    def get(self, request):
        from apps.finance.models import FinanceAccount

        rows = services.with_balance(FinanceAccount.objects.filter(organization=request.organization))
        return Response([_account(a, b) for a, b in rows])

    def post(self, request):
        data = AccountInput(data=request.data)
        data.is_valid(raise_exception=True)
        a = services.create_account(request.tenant, data=data.validated_data, request=request)
        return Response(_account(a, a.opening_balance), status=status.HTTP_201_CREATED)


class AccountDetailView(Base):
    permission_map = {"PATCH": "finance.manage"}

    def patch(self, request, pk):
        data = AccountInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        a = services.update_account(request.tenant, pk, data=data.validated_data, request=request)
        balance = dict((x.pk, b) for x, b in services.with_balance([a]))[a.pk]
        return Response(_account(a, balance))


class CategoryListCreateView(Base):
    permission_map = {"GET": "finance.view", "POST": "finance.manage"}

    def get(self, request):
        services.ensure_default_categories(request.organization)
        rows = FinanceCategory.objects.filter(organization=request.organization)
        return Response([{"id": c.pk, "name": c.name, "kind": c.kind} for c in rows])

    def post(self, request):
        data = CategoryInput(data=request.data)
        data.is_valid(raise_exception=True)
        c = services.create_category(request.tenant, data=data.validated_data, request=request)
        return Response({"id": c.pk, "name": c.name, "kind": c.kind}, status=status.HTTP_201_CREATED)


class TransactionListCreateView(Base):
    permission_map = {"GET": "finance.view", "POST": "finance.create"}

    def get(self, request):
        qs = Transaction.objects.filter(organization=request.organization).select_related("account", "category")
        p = request.query_params
        for param, field in (("kind", "kind"), ("account", "account_id"), ("category", "category_id")):
            if p.get(param):
                qs = qs.filter(**{field: p[param]})
        if p.get("date_from"):
            qs = qs.filter(date__gte=p["date_from"])
        if p.get("date_to"):
            qs = qs.filter(date__lte=p["date_to"])
        if p.get("search"):
            qs = qs.filter(Q(description__icontains=p["search"]))
        paginator = Pagination()
        page = paginator.paginate_queryset(qs, request)
        return paginator.get_paginated_response([_transaction(t) for t in page])

    def post(self, request):
        data = TransactionInput(data=request.data)
        data.is_valid(raise_exception=True)
        t = services.create_transaction(request.tenant, data=data.validated_data, request=request)
        return Response(_transaction(t), status=status.HTTP_201_CREATED)


class TransactionDetailView(Base):
    permission_map = {"PATCH": "finance.update", "DELETE": "finance.delete"}

    def patch(self, request, pk):
        data = TransactionInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        t = services.update_transaction(request.tenant, pk, data=data.validated_data, request=request)
        return Response(_transaction(t))

    def delete(self, request, pk):
        services.delete_transaction(request.tenant, pk, request=request)
        return Response(status=status.HTTP_204_NO_CONTENT)


class SummaryView(Base):
    permission_map = {"GET": "finance.view"}

    def get(self, request):
        def num(name):
            value = request.query_params.get(name)
            return int(value) if value and value.isdigit() else None

        return Response(services.summary(request.tenant, year=num("year"), month=num("month")))


class UnrecordedView(Base):
    permission_map = {"GET": "finance.view"}

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


class RecordSourceView(Base):
    permission_map = {"POST": "finance.create"}

    def post(self, request):
        data = RecordInput(data=request.data)
        data.is_valid(raise_exception=True)
        v = data.validated_data
        t = services.record_source(request.tenant, source=v["source"], source_id=v["source_id"], account_id=v["account"], request=request)
        return Response(_transaction(t), status=status.HTTP_201_CREATED)
