"""HR API v1 — Attendance & Shift endpoints (CBV).

Covers: ShiftDefinition, EmployeeShift, AttendanceRecord (check-in/out, CRUD, summary).
All shift endpoints are gated behind the hr.shift_management feature flag.
"""
from __future__ import annotations

from rest_framework import status
from rest_framework.request import Request
from rest_framework.response import Response

from simorgh.apps.hr_core import selectors as sel
from simorgh.apps.hr_core import services
from simorgh.apps.hr_core.permissions import (
    PERM_ATTENDANCE_MANAGE,
    PERM_ATTENDANCE_VIEW_OWN,
    PERM_ATTENDANCE_VIEW_TEAM,
    PERM_SHIFT_MANAGE,
)
from simorgh.apps.hr_core.serializers import (
    AttendanceRecordSerializer,
    EmployeeShiftSerializer,
    ShiftDefinitionSerializer,
)
from simorgh.core.api.mixins import PermissionCheckMixin
from simorgh.core.api.views import TenantAPIView, TenantListCreateView


def _shift_feature_guard(tenant_pk: int) -> Response | None:
    from simorgh.apps.modules.selectors import is_feature_enabled
    if not is_feature_enabled(tenant_pk, "hr.shift_management"):
        return Response(
            {"detail": "Feature not available.", "feature": "hr.shift_management"},
            status=status.HTTP_403_FORBIDDEN,
        )
    return None


# ---------------------------------------------------------------------------
# Shifts
# ---------------------------------------------------------------------------

class ShiftListCreateView(PermissionCheckMixin, TenantListCreateView):
    """GET/POST /hr/shifts/"""

    def get_required_permission(self, request: Request) -> str | None:
        return PERM_SHIFT_MANAGE if request.method == "POST" else PERM_ATTENDANCE_VIEW_TEAM

    def get(self, request: Request, *args, **kwargs) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        return super().get(request, *args, **kwargs)

    def post(self, request: Request, *args, **kwargs) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        return super().post(request, *args, **kwargs)

    def get_queryset(self):
        from simorgh.apps.hr_core.models import ShiftDefinition
        return ShiftDefinition.objects.filter(tenant=self.tenant).order_by("name")

    def serialize_object(self, obj) -> dict:
        return ShiftDefinitionSerializer(obj).data

    def create_object(self, data: dict):
        from datetime import time as _time
        if not data.get("name"):
            raise ValueError("name is required.")
        def _pt(v):
            return _time.fromisoformat(str(v)) if isinstance(v, str) else v
        return services.create_shift(
            tenant=self.tenant,
            name=data["name"],
            start_time=_pt(data.get("start_time")),
            end_time=_pt(data.get("end_time")),
            work_days=data.get("work_days", [1, 2, 3, 4, 5]),
            break_minutes=int(data.get("break_minutes", 0)),
        )


class ShiftDetailView(PermissionCheckMixin, TenantAPIView):
    """PATCH/DELETE /hr/shifts/<shift_id>/"""

    permission_required = PERM_SHIFT_MANAGE

    def _get_object(self, shift_id: int):
        from simorgh.apps.hr_core.models import ShiftDefinition
        try:
            return ShiftDefinition.objects.get(tenant=self.tenant, pk=shift_id)
        except ShiftDefinition.DoesNotExist:
            return None

    def patch(self, request: Request, shift_id: int) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        shift = self._get_object(shift_id)
        if shift is None:
            return self._not_found("ShiftDefinition")
        data = request.data
        from datetime import time as _time
        def _pt(v):
            return _time.fromisoformat(str(v)) if isinstance(v, str) else v
        update_kwargs = {}
        for k, v in data.items():
            if k not in {"name", "start_time", "end_time", "work_days", "break_minutes"}:
                continue
            if k in {"start_time", "end_time"}:
                update_kwargs[k] = _pt(v)
            else:
                update_kwargs[k] = v
        shift = services.update_shift(shift, **update_kwargs)
        return Response(ShiftDefinitionSerializer(shift).data)

    def delete(self, request: Request, shift_id: int) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        shift = self._get_object(shift_id)
        if shift is None:
            return self._not_found("ShiftDefinition")
        shift.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


# ---------------------------------------------------------------------------
# Employee Shifts
# ---------------------------------------------------------------------------

class EmployeeShiftListCreateView(PermissionCheckMixin, TenantListCreateView):
    """GET/POST /hr/employee-shifts/"""

    def get_required_permission(self, request: Request) -> str | None:
        return PERM_SHIFT_MANAGE if request.method == "POST" else PERM_ATTENDANCE_VIEW_TEAM

    def get(self, request: Request, *args, **kwargs) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        return super().get(request, *args, **kwargs)

    def post(self, request: Request, *args, **kwargs) -> Response:
        denied = _shift_feature_guard(self.tenant.pk)
        if denied:
            return denied
        return super().post(request, *args, **kwargs)

    def get_queryset(self):
        from simorgh.apps.hr_core.models import EmployeeShift
        qp = self.request.query_params
        qs = EmployeeShift.objects.filter(
            employee__tenant=self.tenant
        ).select_related("shift")
        if qp.get("employee"):
            qs = qs.filter(employee_id=int(qp["employee"]))
        return qs.order_by("-effective_from")

    def serialize_object(self, obj) -> dict:
        return EmployeeShiftSerializer(obj).data

    def create_object(self, data: dict):
        from datetime import date
        from simorgh.apps.hr_core.models import Employee, ShiftDefinition
        try:
            employee = Employee.objects.get(tenant=self.tenant, pk=data["employee_id"])
        except (Employee.DoesNotExist, KeyError):
            raise ValueError("employee_id is required and must exist.")
        try:
            shift = ShiftDefinition.objects.get(tenant=self.tenant, pk=data["shift_id"])
        except (ShiftDefinition.DoesNotExist, KeyError):
            raise ValueError("shift_id is required and must exist.")

        return services.assign_shift_to_employee(
            employee=employee,
            shift=shift,
            effective_from=date.fromisoformat(str(data["effective_from"])),
            effective_to=date.fromisoformat(str(data["effective_to"])) if data.get("effective_to") else None,
        )


# ---------------------------------------------------------------------------
# Attendance
# ---------------------------------------------------------------------------

class AttendanceListCreateView(PermissionCheckMixin, TenantListCreateView):
    """GET/POST /hr/attendance/"""

    def get_required_permission(self, request: Request) -> str | None:
        return PERM_ATTENDANCE_MANAGE if request.method == "POST" else PERM_ATTENDANCE_VIEW_TEAM

    def get(self, request: Request, *args, **kwargs) -> Response:
        tenant = self.get_tenant()
        if tenant is None:
            return self._no_tenant()
        return Response([self.serialize_object(obj) for obj in self.get_queryset()])

    def get_queryset(self):
        from datetime import date
        from simorgh.apps.hr_core.models import Employee
        qp = self.request.query_params
        employee = None
        if qp.get("employee"):
            try:
                employee = Employee.objects.get(tenant=self.tenant, pk=int(qp["employee"]))
            except Employee.DoesNotExist:
                pass
        fd = date.fromisoformat(qp["date_from"]) if qp.get("date_from") else None
        td = date.fromisoformat(qp["date_to"]) if qp.get("date_to") else None
        return services.get_attendance_records(
            tenant=self.tenant,
            employee=employee,
            date_from=fd,
            date_to=td,
        )

    def serialize_object(self, obj) -> dict:
        return AttendanceRecordSerializer(obj).data

    def create_object(self, data: dict):
        from datetime import date, time as _time
        from simorgh.apps.hr_core.models import Employee
        def _pt(v):
            return _time.fromisoformat(str(v)) if isinstance(v, str) else v
        try:
            employee = Employee.objects.get(tenant=self.tenant, pk=data["employee_id"])
        except (Employee.DoesNotExist, KeyError):
            raise ValueError("employee_id is required and must exist.")
        return services.create_attendance_record(
            tenant=self.tenant,
            employee=employee,
            date=date.fromisoformat(str(data["date"])),
            check_in=_pt(data.get("check_in")),
            check_out=_pt(data.get("check_out")),
            source=data.get("source", "manual"),
            note=data.get("note", ""),
            break_minutes=int(data.get("break_minutes", 0)),
        )


class AttendanceDetailView(PermissionCheckMixin, TenantAPIView):
    """GET/PATCH /hr/attendance/<public_id>/"""

    permission_required = PERM_ATTENDANCE_VIEW_TEAM

    def _get_object(self, public_id):
        from simorgh.apps.hr_core.models import AttendanceRecord
        try:
            return AttendanceRecord.objects.get(
                employee__tenant=self.tenant, public_id=public_id
            )
        except AttendanceRecord.DoesNotExist:
            return None

    def get(self, request: Request, public_id) -> Response:
        record = self._get_object(public_id)
        if record is None:
            return self._not_found("AttendanceRecord")
        return Response(AttendanceRecordSerializer(record).data)

    def patch(self, request: Request, public_id) -> Response:
        from simorgh.apps.iam.engine import is_allowed
        from simorgh.core.context import current_request_context
        if not is_allowed(current_request_context(), PERM_ATTENDANCE_MANAGE):
            return self._forbidden(f"Permission '{PERM_ATTENDANCE_MANAGE}' is required.")

        record = self._get_object(public_id)
        if record is None:
            return self._not_found("AttendanceRecord")
        data = request.data
        try:
            record = services.update_attendance_record(
                record,
                check_in=data.get("check_in"),
                check_out=data.get("check_out"),
                note=data.get("note"),
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(AttendanceRecordSerializer(record).data)

    def delete(self, request: Request, public_id) -> Response:
        from simorgh.apps.iam.engine import is_allowed
        from simorgh.core.context import current_request_context
        if not is_allowed(current_request_context(), PERM_ATTENDANCE_MANAGE):
            return self._forbidden(f"Permission '{PERM_ATTENDANCE_MANAGE}' is required.")
        record = self._get_object(public_id)
        if record is None:
            return self._not_found("AttendanceRecord")
        record.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


class AttendanceCheckInView(PermissionCheckMixin, TenantAPIView):
    """POST /hr/attendance/check-in/"""

    permission_required = PERM_ATTENDANCE_VIEW_OWN

    def post(self, request: Request) -> Response:
        from datetime import date as _date, time as _time
        from simorgh.apps.hr_core.models import Employee
        data = request.data
        employee_id = data.get("employee_id")
        if employee_id:
            try:
                employee = Employee.objects.get(tenant=self.tenant, pk=int(employee_id))
            except Employee.DoesNotExist:
                return self._not_found("Employee")
        else:
            try:
                employee = Employee.objects.get(tenant=self.tenant, user_id=request.user.pk)
            except Employee.DoesNotExist:
                return self._not_found("Employee")
        record_date = _date.fromisoformat(str(data["date"])) if data.get("date") else _date.today()
        record_time = _time.fromisoformat(str(data["time"])) if data.get("time") else None
        if record_time is None:
            from datetime import datetime, timezone
            record_time = datetime.now(tz=timezone.utc).time()
        try:
            record = services.check_in_employee(
                tenant=self.tenant,
                employee=employee,
                date=record_date,
                time=record_time,
                source=data.get("source", "web"),
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(AttendanceRecordSerializer(record).data, status=status.HTTP_200_OK)


class AttendanceCheckOutView(PermissionCheckMixin, TenantAPIView):
    """POST /hr/attendance/check-out/"""

    permission_required = PERM_ATTENDANCE_VIEW_OWN

    def post(self, request: Request) -> Response:
        from datetime import date as _date, time as _time
        from simorgh.apps.hr_core.models import Employee
        data = request.data
        employee_id = data.get("employee_id")
        if employee_id:
            try:
                employee = Employee.objects.get(tenant=self.tenant, pk=int(employee_id))
            except Employee.DoesNotExist:
                return self._not_found("Employee")
        else:
            try:
                employee = Employee.objects.get(tenant=self.tenant, user_id=request.user.pk)
            except Employee.DoesNotExist:
                return self._not_found("Employee")
        record_date = _date.fromisoformat(str(data["date"])) if data.get("date") else _date.today()
        record_time = _time.fromisoformat(str(data["time"])) if data.get("time") else None
        if record_time is None:
            from datetime import datetime, timezone
            record_time = datetime.now(tz=timezone.utc).time()
        try:
            record = services.check_out_employee(
                tenant=self.tenant,
                employee=employee,
                date=record_date,
                time=record_time,
            )
        except ValueError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(AttendanceRecordSerializer(record).data, status=status.HTTP_200_OK)


class AttendanceMonthlySummaryView(PermissionCheckMixin, TenantAPIView):
    """GET /hr/attendance/summary/"""

    permission_required = PERM_ATTENDANCE_VIEW_OWN

    def get(self, request: Request) -> Response:
        qp = request.query_params
        employee_id = qp.get("employee")
        year = qp.get("year")
        month = qp.get("month")

        if not year or not month:
            return Response(
                {"detail": "year and month query params are required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Admins can query any employee; ESS users can only query themselves
        if employee_id:
            from simorgh.apps.iam.engine import is_allowed
            from simorgh.core.context import current_request_context
            if not is_allowed(current_request_context(), PERM_ATTENDANCE_VIEW_TEAM):
                return self._forbidden(f"Permission '{PERM_ATTENDANCE_VIEW_TEAM}' is required.")

        from simorgh.apps.hr_core.models import Employee
        if employee_id:
            try:
                employee = Employee.objects.get(tenant=self.tenant, pk=int(employee_id))
            except Employee.DoesNotExist:
                return self._not_found("Employee")
        else:
            try:
                employee = Employee.objects.get(tenant=self.tenant, user_id=request.user.pk)
            except Employee.DoesNotExist:
                return self._not_found("Employee")

        summary = services.get_monthly_summary(
            tenant=self.tenant,
            employee=employee,
            year=int(year),
            month=int(month),
        )
        return Response(summary)
