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.workplace import services
from apps.workplace.models import AllowedNetwork, LocationExemption, PunchLog, WorkPlace

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


class Base(APIView):
    permission_classes = [TenantPermission]
    module_code = "attendance"
    allow_implicit_org = True


class Settings(Base):
    """Manager-only endpoints (attendance.manage_settings at organization scope)."""

    scope_map = ORG


def _policy(p) -> dict:
    return {"enabled": p.enabled, "use_location": p.use_location, "use_network": p.use_network,
            "max_accuracy_m": p.max_accuracy_m, "apply_to_checkout": p.apply_to_checkout}


def _place(p: WorkPlace) -> dict:
    return {"id": p.pk, "name": p.name, "latitude": p.latitude, "longitude": p.longitude, "radius_m": p.radius_m, "is_active": p.is_active}


def _network(n: AllowedNetwork) -> dict:
    return {"id": n.pk, "label": n.label, "cidr": n.cidr}


def _exemption(e: LocationExemption) -> dict:
    return {"id": e.pk, "user": {"id": e.user_id, "name": e.user.name or e.user.username}, "start_date": e.start_date,
            "end_date": e.end_date, "reason": e.reason}


def _punch(p: PunchLog) -> dict:
    reviewer = p.reviewed_by
    return {
        "id": p.pk, "user_id": p.user_id, "user_name": p.user.name or p.user.username, "kind": p.kind,
        "date": p.date, "time": p.time.strftime("%H:%M:%S"), "verdict": p.verdict, "status": p.status,
        "latitude": p.latitude, "longitude": p.longitude, "accuracy_m": p.accuracy_m, "distance_m": p.distance_m,
        "place_name": p.place_name, "ip_address": p.ip_address, "review_note": p.review_note,
        "reviewed_by_name": (reviewer.name or reviewer.username) if reviewer else None, "reviewed_at": p.reviewed_at,
    }


class PolicyInput(serializers.Serializer):
    enabled = serializers.BooleanField(required=False)
    use_location = serializers.BooleanField(required=False)
    use_network = serializers.BooleanField(required=False)
    apply_to_checkout = serializers.BooleanField(required=False)
    max_accuracy_m = serializers.IntegerField(required=False)


class PlaceInput(serializers.Serializer):
    name = serializers.CharField(max_length=120, required=False)
    latitude = serializers.FloatField(required=False)
    longitude = serializers.FloatField(required=False)
    radius_m = serializers.IntegerField(required=False)
    is_active = serializers.BooleanField(required=False)


class NetworkInput(serializers.Serializer):
    label = serializers.CharField(max_length=120)
    cidr = serializers.CharField(max_length=64)


class ExemptionInput(serializers.Serializer):
    user = serializers.IntegerField()
    start_date = serializers.DateField(required=False, allow_null=True)
    end_date = serializers.DateField(required=False, allow_null=True)
    reason = serializers.CharField(required=False, allow_blank=True, max_length=200)


class PolicyView(Settings):
    permission_map = {"GET": "attendance.manage_settings", "PUT": "attendance.manage_settings"}

    def get(self, request):
        return Response(_policy(services.get_policy(request.organization)))

    def put(self, request):
        data = PolicyInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        return Response(_policy(services.update_policy(request.tenant, data=data.validated_data, request=request)))


class PlaceListCreateView(Settings):
    permission_map = {"GET": "attendance.manage_settings", "POST": "attendance.manage_settings"}

    def get(self, request):
        return Response([_place(p) for p in WorkPlace.objects.filter(organization=request.organization)])

    def post(self, request):
        data = PlaceInput(data=request.data)
        data.is_valid(raise_exception=True)
        return Response(_place(services.create_place(request.tenant, data=data.validated_data, request=request)), status=status.HTTP_201_CREATED)


class PlaceDetailView(Settings):
    permission_map = {"PATCH": "attendance.manage_settings", "DELETE": "attendance.manage_settings"}

    def patch(self, request, pk):
        data = PlaceInput(data=request.data, partial=True)
        data.is_valid(raise_exception=True)
        return Response(_place(services.update_place(request.tenant, pk, data=data.validated_data, request=request)))

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


class NetworkListCreateView(Settings):
    permission_map = {"GET": "attendance.manage_settings", "POST": "attendance.manage_settings"}

    def get(self, request):
        return Response([_network(n) for n in AllowedNetwork.objects.filter(organization=request.organization)])

    def post(self, request):
        data = NetworkInput(data=request.data)
        data.is_valid(raise_exception=True)
        row = services.add_network(request.tenant, request=request, **data.validated_data)
        return Response(_network(row), status=status.HTTP_201_CREATED)


class NetworkDetailView(Settings):
    permission_map = {"DELETE": "attendance.manage_settings"}

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


class ExemptionListCreateView(Settings):
    permission_map = {"GET": "attendance.manage_settings", "POST": "attendance.manage_settings"}

    def get(self, request):
        rows = LocationExemption.objects.filter(organization=request.organization).select_related("user")
        return Response([_exemption(e) for e in rows])

    def post(self, request):
        data = ExemptionInput(data=request.data)
        data.is_valid(raise_exception=True)
        v = data.validated_data
        row = services.add_exemption(
            request.tenant, user_id=v["user"], start_date=v.get("start_date"), end_date=v.get("end_date"),
            reason=v.get("reason", ""), request=request,
        )
        return Response(_exemption(row), status=status.HTTP_201_CREATED)


class ExemptionDetailView(Settings):
    permission_map = {"DELETE": "attendance.manage_settings"}

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


class MyIpView(Settings):
    """So a manager can add the office address they are currently on."""

    permission_map = {"GET": "attendance.manage_settings"}

    def get(self, request):
        return Response({"ip": services.client_ip(request)})


class StatusView(Base):
    """For everyone: does this person need to send a location right now?"""

    permission_map = {"GET": "attendance.view"}

    def get(self, request):
        return Response(services.requirement(request.organization, request.user, request))


class PunchListView(Settings):
    permission_map = {"GET": "attendance.approve"}

    def get(self, request):
        qs = PunchLog.objects.filter(organization=request.organization).select_related("user", "reviewed_by")
        wanted = request.query_params.get("status", "pending")
        if wanted == "reviewed":
            qs = qs.exclude(status="pending").order_by("-reviewed_at", "-id")
        elif wanted != "all":
            qs = qs.filter(status=wanted)
        return Response([_punch(p) for p in qs[:200]])


class PunchReviewView(Settings):
    permission_map = {"POST": "attendance.approve"}
    approve = True

    def post(self, request, pk):
        note = str(request.data.get("note") or "")
        punch = services.review_punch(request.tenant, pk, approve=self.approve, note=note, request=request)
        return Response(_punch(punch))
