from django.utils import timezone
from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models import EsExamRequirement, EsExamRequirementUnavailability
from api.models.audit_trail import AuditTrail
from api.models.audit_trail_details import AuditTrailDetails
from api.translation import __
from api.utils import get_exception_detail, get_ip, log_critical_error
from api.validator import BaseValidator
from api.views.admin.base import AdminApiBase


class ExamRequirementUpdateUnavailability(AdminApiBase):
    def validate_request(self, request):
        rules = {
            "id": "required|array|exists:api.EsExamRequirement,id",
            "time_slot": "nullable|array",
        }

        attribute = {
            "id": "ID",
            "time_slot": __("attr.time_slot"),
        }

        validator = BaseValidator(request.data, rules, attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

        exam_requirement_ids = request.data.get("id") or []

        time_slots = request.data.get("time_slot") or []
        if time_slots:
            rules = {
                "time_slot": "required|integer|gte:0",
            }

            for time_slot in time_slots:
                validator = BaseValidator(
                    {
                        "time_slot": time_slot,
                    },
                    rules,
                    attribute,
                )
                error = validator.validate()
                if error:
                    raise ValidationError(error)

        return (
            EsExamRequirement.objects
            .prefetch_related("esexamrequirementunavailability_set")
            .filter(id__in=exam_requirement_ids)
        )

    def post(self, request):
        try:
            edit_obj = self.validate_request(request)

            action = "exam_requirement_update_unavailability"
            old_new_data = {
                "new_data": {},
                "old_data": {},
            }

            time_slots = request.data.get("time_slot") or []
            time_slots = [int(value) for value in dict.fromkeys(time_slots)]

            for obj in edit_obj:
                existing_time_slots = set(
                    EsExamRequirementUnavailability.objects.filter(
                        exam_requirement_id=obj.id,
                    ).values_list("time_slot", flat=True)
                )

                if set(existing_time_slots) != set(time_slots):
                    old_new_data.setdefault("old_data", {}).setdefault(obj.id, {})["time_slot"] = sorted(existing_time_slots)
                    old_new_data.setdefault("new_data", {}).setdefault(obj.id, {})["time_slot"] = sorted(time_slots)

                    EsExamRequirementUnavailability.objects.filter(
                        exam_requirement_id=obj.id,
                    ).delete()

                    if time_slots:
                        EsExamRequirementUnavailability.bulk_insert(obj.id, time_slots, request.user.id)

                        EsExamRequirementUnavailability.objects.filter(
                            exam_requirement_id=obj.id,
                            time_slot__in=time_slots,
                        ).update(
                            updated_by=request.user.id,
                            updated_at=timezone.now(),
                        )

            if old_new_data["old_data"] or old_new_data["new_data"]:
                audit_trail = AuditTrail.objects.create(
                    user_id=request.user.id,
                    type=self.audit_type,
                    ip_address=get_ip(request),
                )

                remark_param = {
                    "code": ",".join(edit_obj.values_list("code", flat=True)),
                }

                AuditTrailDetails.custom_insert(
                    audit_trail=audit_trail,
                    action=action,
                    remark_param=remark_param,
                    new_data=old_new_data["new_data"],
                    old_data=old_new_data["old_data"],
                )

            response = {

            }
            return self.api_response(data=response)
        except ValidationError as e:
            first_message = e.detail["error"]
            errors = e.detail["errors"]
            return self.api_response(error=first_message, errors=errors, code=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            e_details = get_exception_detail(e)
            log_critical_error(user_id=None, descr=e_details["descr"], url=e_details["url"], trace=e_details["trace"])
            return self.api_response(error=__("message.internal_server_error"), code=status.HTTP_500_INTERNAL_SERVER_ERROR)