from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models import AuditTrail, AuditTrailDetails, EsInvigilatorSetting
from api.services.invigilator_assign import estimate_required_invigilators
from api.services.invigilator_setting_v2 import load_invigilator_setting, 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 InvigilatorSettingUpdateV2(AdminApiBase):
    def validate_request(self, request):
        attribute = {
            "configuration": __("attr.configuration"),
            "id": "ID",
            "role_ids": __("attr.role_ids"),
            "is_floating": __("attr.is_floating"),
            "is_inclusive": __("attr.is_inclusive"),
            "quantity_mode": __("attr.quantity_mode"),
            "quantity": __("attr.quantity"),
            "students_count": __("attr.students_count"),
            "priority": __("attr.priority"),
        }
        is_floating_values = ",".join(str(value) for value in EsInvigilatorSetting.IS_FLOATING_TO_CODE.values())
        is_inclusive_values = ",".join(str(value) for value in EsInvigilatorSetting.IS_INCLUSIVE_TO_CODE.values())
        quantity_mode_values = ",".join(str(value) for value in EsInvigilatorSetting.QUANTITY_MODE_TO_CODE.values())
        ratio_mode = EsInvigilatorSetting.QUANTITY_MODE_TO_CODE["ratio"]

        validator = BaseValidator(request.data, {"configuration": "nullable|array"}, attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

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

            row_validator = BaseValidator(
                row,
                {
                    "id": "nullable|integer|exists:api.EsInvigilatorSetting,id",
                    "role_ids": "nullable|array",
                    "is_floating": f"nullable|integer|in:{is_floating_values}",
                    "is_inclusive": f"nullable|integer|in:{is_inclusive_values}",
                    "quantity_mode": f"required|integer|in:{quantity_mode_values}",
                    "quantity": "required|integer|gte:0",
                    "students_count": "nullable|integer",
                    "priority": "nullable|integer",
                },
                attribute,
            )
            row_error = row_validator.validate()
            if row_error:
                raise ValidationError(row_error)

            quantity = int(row.get("quantity") or 0)
            quantity_mode = int(row.get("quantity_mode"))
            any_role_id = EsInvigilatorSetting.ANY_ROLE_ID
            raw_role_ids = row.get("role_ids") or []
            if not isinstance(raw_role_ids, list):
                raw_role_ids = []
            real_role_ids = [
                role_id for role_id in raw_role_ids
                if role_id not in (None, "", any_role_id, str(any_role_id))
            ]
            if real_role_ids:
                role_validator = BaseValidator(
                    {"role_ids": real_role_ids},
                    {"role_ids": "array|exists:api.EsInvigilatorRole,id"},
                    attribute,
                )
                role_error = role_validator.validate()
                if role_error:
                    raise ValidationError(role_error)
            role_ids = []
            for role_id in real_role_ids:
                parsed = int(role_id)
                if parsed not in role_ids:
                    role_ids.append(parsed)

            students_count = row.get("students_count")
            if quantity_mode == ratio_mode:
                students_validator = BaseValidator(
                    {"students_count": students_count},
                    {"students_count": "required|integer|gte:1"},
                    attribute,
                )
                students_error = students_validator.validate()
                if students_error:
                    raise ValidationError(students_error)
                students_count = int(students_count)
            else:
                students_count = None

            normalized = {
                "role_ids": role_ids,
                "is_floating": int(row.get("is_floating") or 0),
                "is_inclusive": int(row.get("is_inclusive") or 0),
                "quantity_mode": quantity_mode,
                "quantity": quantity,
                "students_count": students_count,
                "priority": int(row.get("priority") or 0),
            }
            setting_id = row.get("id")
            if setting_id not in (None, ""):
                normalized["id"] = int(setting_id)
            normalized_rows.append(normalized)

        return {"configuration": normalized_rows}

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

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

            response_data = dict(new_data)
            response_data["total_estimated_invigilators"] = estimate_required_invigilators()
            return self.api_response(data=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)
