from __future__ import annotations

from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.access.drf import TenantPermission
from apps.payroll import services
from apps.payroll.models import PayrollItem, PayrollRun
from interfaces.api_v1.serializers import UserBriefSerializer

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


class RunSerializer(serializers.ModelSerializer):
    class Meta:
        model = PayrollRun
        fields = ["id", "year", "month", "status", "created_at", "approved_at", "hourly_divisor", "overtime_multiplier", "deduct_lateness"]


class ItemSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()

    class Meta:
        model = PayrollItem
        fields = [
            "id", "user", "pay_type", "unit_rate", "paid_days", "base_salary", "worked_minutes", "late_minutes", "overtime_minutes",
            "overtime_pay", "late_deduction", "additions", "deductions", "net", "notes",
        ]

    def get_user(self, obj):
        return UserBriefSerializer(obj.membership.user).data


class RunInput(serializers.Serializer):
    year = serializers.IntegerField()
    month = serializers.IntegerField()


class ItemInput(serializers.Serializer):
    additions = serializers.IntegerField(required=False)
    deductions = serializers.IntegerField(required=False)
    notes = serializers.CharField(max_length=300, required=False, allow_blank=True)


class SettingsInput(serializers.Serializer):
    monthly_standard_hours = serializers.IntegerField(required=False)
    overtime_multiplier = serializers.DecimalField(max_digits=4, decimal_places=2, required=False)
    deduct_lateness = serializers.BooleanField(required=False)


def _settings_data(s) -> dict:
    return {
        "monthly_standard_hours": s.monthly_standard_hours,
        "overtime_multiplier": str(s.overtime_multiplier),
        "deduct_lateness": s.deduct_lateness,
    }


class RunListCreateView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"GET": "payroll.view", "POST": "payroll.manage"}
    scope_map = {"POST": "organization"}

    def get(self, request):
        return Response(RunSerializer(services.visible_runs(request.tenant), many=True).data)

    def post(self, request):
        data = RunInput(data=request.data)
        data.is_valid(raise_exception=True)
        run = services.create_run(request.tenant, request=request, **data.validated_data)
        return Response(RunSerializer(run).data, status=status.HTTP_201_CREATED)


class RunDetailView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"GET": "payroll.view", "DELETE": "payroll.manage"}
    scope_map = {"DELETE": "organization"}

    def get(self, request, pk):
        run = services.get_run(request.tenant, pk)
        items = services.visible_items(request.tenant, run)
        return Response({**RunSerializer(run).data, "items": ItemSerializer(items, many=True).data})

    def delete(self, request, pk):
        services.delete_run(request.tenant, services.get_run(request.tenant, pk, for_update=True), request=request)
        return Response(status=status.HTTP_204_NO_CONTENT)


class RunCalculateView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"POST": "payroll.manage"}
    scope_map = ORG

    def post(self, request, pk):
        run = services.get_run(request.tenant, pk, for_update=True)
        return Response(services.calculate_run(request.tenant, run, request=request))


class RunApproveView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"POST": "payroll.approve"}
    scope_map = ORG

    def post(self, request, pk):
        run = services.get_run(request.tenant, pk, for_update=True)
        return Response(RunSerializer(services.approve_run(request.tenant, run, request=request)).data)


class ItemDetailView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"PATCH": "payroll.manage"}
    scope_map = ORG

    def patch(self, request, pk):
        item = services.get_item(request.tenant, pk)
        data = ItemInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        return Response(ItemSerializer(services.update_item(request.tenant, item, data=data.validated_data, request=request)).data)


class SettingsView(APIView):
    permission_classes = [TenantPermission]
    module_code = "payroll"
    permission_map = {"GET": "payroll.settings", "PUT": "payroll.settings"}
    scope_map = {"GET": "organization", "PUT": "organization"}

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

    def put(self, request):
        data = SettingsInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        return Response(_settings_data(services.update_settings(request.tenant, data=data.validated_data, request=request)))
