from rest_framework import status
from rest_framework.exceptions import ValidationError
from django.db.models import Count, F, IntegerField, ExpressionWrapper, CharField
from django.db.models.functions import Cast
from api.models import EsSuitability
from api.models.es_location import EsLocation
from api.translation import __
from api.utils import log_critical_error, get_exception_detail
from api.views.admin.base import AdminApiBase

class LocationList(AdminApiBase):
    list_default_order_column = "id"

    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):
        objs = (
            EsLocation.objects
            .select_related("zone")
            .prefetch_related("eslocationunavailableseat_set")
            .annotate(
                total_seats=ExpressionWrapper(
                    F("row") * F("column") - Count("eslocationunavailableseat", distinct=True),
                    output_field=IntegerField(),
                )
            )
            .all()
        )
        objs = self.location_listing_filter(request, objs)
        pagination = self.location_listing_pagination(request, objs)

        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["seat_order"] = val.seat_order
            data["seat_order_text"] = __("attr.location_seat_order." + str(val.seat_order))
            data["row"] = val.row
            data["column"] = val.column
            data["total_seats"] = val.total_seats
            data["is_partition"] = val.is_partition
            data['zone'] = val.zone.name if val.zone else None
            data['zone_id'] = val.zone_id
            data["status"] = val.status
            data["status_text"] = __("attr.location_status." + str(val.status))

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

            data['unavailable_seats'] = [
                {
                    "type": unavailable_seat.type,
                    "type_text": __(f"attr.location_unavailable_seat_type.{unavailable_seat.type}"),
                    "x": unavailable_seat.cord_x,
                    "y": unavailable_seat.cord_y
                }
                for unavailable_seat in val.eslocationunavailableseat_set.order_by("cord_x", "cord_y").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 location_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" | "description":
                    field_name = "desc" if key == "description" else key
                    model = model.filter(**{f"{field_name}__icontains": val})
                case "row_min":
                    try:
                        model = model.filter(row__gte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "row_max":
                    try:
                        model = model.filter(row__lte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "column_min":
                    try:
                        model = model.filter(column__gte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "column_max":
                    try:
                        model = model.filter(column__lte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "total_seats":
                    model = model.annotate(_txt_total_seats=Cast('total_seats', CharField())).filter(_txt_total_seats__icontains=str(val))
                case "total_seats_min":
                    try:
                        model = model.filter(total_seats__gte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "total_seats_max":
                    try:
                        model = model.filter(total_seats__lte=int(val))
                    except (TypeError, ValueError):
                        continue
                case "is_partition":
                    if isinstance(val, str):
                        normalized = val.strip().lower()
                        if normalized in ("true", "1", "yes", "y"):
                            model = model.filter(is_partition=True)
                        elif normalized in ("false", "0", "no", "n"):
                            model = model.filter(is_partition=False)
                    else:
                        model = model.filter(is_partition=bool(val))
                case "seat_order" | "status":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(**{f"{key}__in": list(val)})
                    else:
                        model = model.filter(**{key: val})
                case "zone" | "zone_id":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(zone_id__in=list(val))
                    else:
                        model = model.filter(zone_id=val)
                case "suitability":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(suitability__in=list(val))
                    else:
                        model = model.filter(suitability__id=val)
        return model

    def location_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()

        if sort_by:
            if str(order_by).lower() == "desc":
                ordering_string = f"-{sort_by}"
            else:
                ordering_string = sort_by
            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,
        }