from rest_framework import status
from rest_framework.exceptions import ValidationError

from django.db.models import Min

from api.models import EsSuitability
from api.translation import __
from api.utils import log_critical_error, get_exception_detail
from api.views.admin.base import AdminApiBase


class SuitabilityList(AdminApiBase):
    list_default_order_column = "name"

    def post(self, request):
        try:
            self.api_log_skip_outgoing_data = True
            data = self.get_data(request)
            return self.api_response(data=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)

    def get_data(self, request):
        suitability = (
            EsSuitability.objects
            .select_related("department")
            .prefetch_related(
                "invigilator",
                "location",
                "tag__tag"
            )
            .annotate(
                _min_invigilator_id=Min("invigilator__id"),
                _min_location_id=Min("location__id"),
                _min_tag_id=Min("tag__tag_id"),
            )
        )
        suitability = self.suitability_listing_filter(request, suitability)
        pagination = self.suitability_listing_pagination(request, suitability)

        return_data = []

        for val in pagination["paginated_data"]:
            data = {}
            data["id"] = val.id
            data["code"] = val.code
            data["name"] = val.name
            data["desc"] = val.desc
            data["department_id"] = val.department_id
            data["department"] = val.department.name if val.department else None
            data["status"] = val.status
            data["status_text"] = __("attr.suitability_status." + str(val.status))

            data["invigilator"] = [
                {"id": invigilator.id, "name": invigilator.name}
                for invigilator in val.invigilator.all()
            ]

            data["location"] = [
                {"id": location.id, "name": location.name}
                for location in val.location.all()
            ]
            data["tag"] = [
                {"id": tag.tag.id, "name": tag.tag.name}
                for tag in val.tag.all()
            ]

            for i in range(1, 11):
                extra_data_field = f"extra_data_{i}"
                data[extra_data_field] = getattr(val, extra_data_field, None)

            return_data.append(data)

        return {
            "data": return_data,
            "total": pagination["total"],
            "page": pagination["page"],
            "per_page": pagination["per_page"],
        }

    def suitability_listing_filter(self, request, model):
        filters = request.data.get("filter")
        if not isinstance(filters, dict) or not filters:
            return model

        for key, val in filters.items():
            if val in (None, "", [], {}):
                continue

            match key:
                case "code" | "name" | "desc":
                    model = model.filter(**{f"{key}__icontains": val})
                case "department_id":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(department_id__in=list(val))
                    else:
                        model = model.filter(department_id=val)
                case "status":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(status__in=list(val))
                    else:
                        model = model.filter(status=val)
                case "invigilator_id":
                    model = model.filter(invigilator__id=val)
                case "location_id":
                    model = model.filter(location__id=val)
                case "tag_id":
                    model = model.filter(tag__tag_id=val)
                case _:
                    continue

        return model

    def suitability_listing_pagination(self, request, model):
        page, per_page = self.parse_pagination(request)
        sort_by = request.data.get("sort_by")
        order_by = request.data.get("order_by")

        total = model.count()

        sort_field_map = {
            "department_id": "department_id",
            "invigilator_id": "_min_invigilator_id",
            "location_id": "_min_location_id",
            "tag_id": "_min_tag_id",
        }

        if sort_by:
            sort_column = sort_field_map.get(sort_by, sort_by)
            if str(order_by).lower() == "desc":
                ordering_string = f"-{sort_column}"
            else:
                ordering_string = sort_column
            model = model.order_by(ordering_string)
        else:
            model = model.order_by(self.list_default_order_column)

        if per_page != -1:
            start_index = (page - 1) * per_page
            end_index = start_index + per_page
            paginated_data = model[start_index:end_index]
        else:
            paginated_data = model

        return {
            "total": total,
            "paginated_data": paginated_data,
            "page": page,
            "per_page": per_page,
        }