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

from api.admin_permission_conf import PERMISSIONS
from api.models import Role, RolePermission, RolePermissionDepartment, TtDepartment
from api.views.admin.base import AdminApiBase
from api.validator import BaseValidator
from api.utils import log_critical_error, get_exception_detail
from api.translation import __


class RoleUpdate(AdminApiBase):
    def validate_request(self, request):
        rules = {
            "id": "required|array|exists:api.Role,id",
        }

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

        edit_role = Role.objects.filter(id__in=request.data["id"])

        rules = {
            "name": "nullable",
            "color": "nullable",
            "type": "nullable",
            "is_only_self": "nullable|boolean",
            "permissions": "nullable|array",
        }

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

        if "permissions" in request.data:
            permissions = request.data.get("permissions")

            if permissions is not None and not isinstance(permissions, list):
                raise ValidationError({
                    "error": __('validation.invalid_format'),
                    "errors": {"permissions": __("validation.invalid_format")}
                })

            if permissions is not None:
                valid_permission_codes = PERMISSIONS
                seen = set()

                for idx, item in enumerate(permissions):
                    if not isinstance(item, dict):
                        raise ValidationError({
                            "error": __('validation.invalid_format'),
                            "errors": {f"permissions.{idx}": __("validation.invalid_format")}
                        })

                    permission = item.get("permission")
                    grant = item.get("grant_to_all_department")
                    department_ids = item.get("department_ids", [])

                    if permission not in valid_permission_codes:
                        raise ValidationError({
                            "error": __('validation.exists'),
                            "errors": {
                                f"permissions.{idx}.permission": __("validation.exists")
                            }
                        })

                    if permission in seen:
                        raise ValidationError({
                            "error": __('validation.duplicate'),
                            "errors": {
                                f"permissions.{idx}.permission": __("validation.duplicate")
                            }
                        })
                    seen.add(permission)

                    if not isinstance(grant, bool):
                        raise ValidationError({
                            "error": __('validation.invalid_format'),
                            "errors": {
                                f"permissions.{idx}.grant_to_all_department": __("validation.invalid_format")
                            }
                        })

                    if department_ids is None:
                        department_ids = []

                    if not isinstance(department_ids, list):
                        raise ValidationError({
                            "error": __('validation.invalid_format'),
                            "errors": {
                                f"permissions.{idx}.department_ids": __("validation.invalid_format")
                            }
                        })

                    if not grant:
                        exists_count = TtDepartment.objects.filter(id__in=department_ids).count()
                        if exists_count != len(set(department_ids)):
                            raise ValidationError({
                                "error": __('validation.exists'),
                                "errors": {
                                    f"permissions.{idx}.department_ids": __("validation.exists")
                                }
                            })

        return edit_role

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

            updated = False
            update_fields = {}
            ignore_fields = [
                "id",
                "timestamp",
                "signature",
                "permissions",
                "is_only_self",
            ]

            if edit_role.count() > 1:
                ignore_fields.append("type")

            valid_fields = {f.column for f in Role._meta.fields}
            for key, val in request.data.items():
                if key not in ignore_fields and key in valid_fields:
                    updated = True
                    update_fields[key] = val

            if "is_only_self" in request.data:
                updated = True
                update_fields["is_only_self"] = request.data.get("is_only_self", False)

            with transaction.atomic():
                if updated and update_fields:
                    update_fields["updated_by"] = request.user.id
                    edit_role.update(**update_fields)

                if edit_role.count() == 1 and "permissions" in request.data:
                    role = edit_role.first()
                    permissions = request.data.get("permissions")
                    if permissions is None:
                        permissions = []

                    RolePermissionDepartment.objects.filter(role_permission__role=role).delete()
                    RolePermission.objects.filter(role=role).delete()

                    for item in permissions:
                        role_permission = RolePermission.objects.create(
                            role=role,
                            permission=item["permission"],
                            grant_to_all_department=item["grant_to_all_department"],
                        )

                        if not item["grant_to_all_department"]:
                            objs = [
                                RolePermissionDepartment(
                                    role_permission=role_permission,
                                    department_id=department_id
                                )
                                for department_id in item.get("department_ids", [])
                            ]
                            if objs:
                                RolePermissionDepartment.objects.bulk_create(objs)

            return self.api_response(data={})

        except ValidationError as e:
            return self.api_response(
                error=e.detail["error"],
                errors=e.detail["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
            )