from django.db import transaction
from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models import AuditTrail, AuditTrailDetails, EsExamRequirement, EsExamRequirementSameTime, EsExamRequirementSameTimeGroup
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.exam_requirement_relation_grouping import (
    update_requirement_relations,
    validate_relation_exam_activity_ids,
)
from api.views.admin.base import AdminApiBase


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

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

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

        validate_relation_exam_activity_ids(request.data)

        exam_requirement_ids = request.data.get("id") or []
        return EsExamRequirement.objects.filter(id__in=exam_requirement_ids)

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

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

            payload_exam_activity_ids = list(dict.fromkeys(request.data.get("exam_activity_id") or []))
            has_changes = False

            for obj in edit_obj:
                update_result = update_requirement_relations(
                    EsExamRequirementSameTime,
                    EsExamRequirementSameTimeGroup,
                    obj.id,
                    payload_exam_activity_ids,
                )

                if update_result["changed"]:
                    has_changes = True
                    old_new_data.setdefault("old_data", {}).setdefault(obj.id, {})["exam_activity_id"] = update_result["old_exam_activity_ids"]
                    old_new_data.setdefault("new_data", {}).setdefault(obj.id, {})["exam_activity_id"] = update_result["new_exam_activity_ids"]

            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"],
                )

            return self.api_response(data={})
        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)