from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models import AuditTrail, AuditTrailDetails
from api.services.invigilator_setting import load_invigilator_setting, parse_bool, save_invigilator_setting
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 InvigilatorSettingUpdate(AdminApiBase):
    def validate_request(self, request):
        attribute = {
            "fixed_invigilators": __("attr.fixed_invigilators"),
            "students_count": __("attr.students_count"),
            "invigilators_count": __("attr.invigilators_count"),
            "include_fixed_invigilators": __("attr.include_fixed_invigilators"),
            "quantity": __("attr.quantity"),
            "role_ids": __("attr.role_ids"),
        }

        rules = {
            "fixed_invigilators": "nullable|array",
            "students_count": "required|integer|gte:0",
            "invigilators_count": "required|integer|gte:0",
            "include_fixed_invigilators": "required",
        }
        validator = BaseValidator(request.data, rules, attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

        include_fixed = request.data.get("include_fixed_invigilators")
        if parse_bool(include_fixed, None) is None and include_fixed not in (False, True, 0, 1, "0", "1"):
            message = __("validation.invalid", field=attribute["include_fixed_invigilators"])
            raise ValidationError({
                "error": message,
                "errors": {
                    "include_fixed_invigilators": [message],
                },
            })

        rows = request.data.get("fixed_invigilators") or []
        normalized_rows = []
        for index, row in enumerate(rows):
            if not isinstance(row, dict):
                message = __("validation.invalid", field=attribute["fixed_invigilators"])
                raise ValidationError({
                    "error": message,
                    "errors": {
                        "fixed_invigilators": [message],
                    },
                })

            row_validator = BaseValidator(
                row,
                {
                    "quantity": "required|integer|gte:0",
                    "role_ids": "nullable|array|exists:api.EsInvigilatorRole,id",
                },
                attribute,
            )
            row_error = row_validator.validate()
            if row_error:
                raise ValidationError(row_error)

            quantity = int(row.get("quantity") or 0)
            role_ids = row.get("role_ids") or []
            if quantity > 0 and not role_ids:
                message = __("validation.required", field=attribute["role_ids"])
                raise ValidationError({
                    "error": message,
                    "errors": {
                        f"fixed_invigilators.{index}.role_ids": [message],
                    },
                })

            normalized_rows.append({
                "quantity": quantity,
                "role_ids": role_ids,
            })

        return {
            "fixed_invigilators": normalized_rows,
            "students_count": int(request.data.get("students_count") or 0),
            "invigilators_count": int(request.data.get("invigilators_count") or 0),
            "include_fixed_invigilators": parse_bool(include_fixed, False),
        }

    def post(self, request):
        try:
            payload = self.validate_request(request)
            old_data = load_invigilator_setting()
            new_data = save_invigilator_setting(payload)

            if old_data != new_data:
                audit_trail = AuditTrail.objects.create(
                    user_id=request.user.id,
                    type=self.audit_type,
                    ip_address=get_ip(request),
                )
                AuditTrailDetails.custom_insert(
                    audit_trail=audit_trail,
                    action="invigilator_setting_update",
                    remark_param={},
                    new_data=new_data,
                    old_data=old_data,
                )

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