from rest_framework import status
from rest_framework.exceptions import ValidationError
from datetime import datetime

from api.models import EsSessionStart
from api.translation import __
from api.utils import log_critical_error, get_exception_detail
from api.validator import BaseValidator
from api.views.admin.base import AdminApiBase

class SessionStartList(AdminApiBase):
    list_default_order_column = "id"

    def validate_request(self,request):
        rules = {
            "exam_period_id": "required|exists:api.EsExamPeriod,id",
        }

        # if need rename field, can put here
        attribute = {
            # "email": __("attr.email"),
        }

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

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

            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):
        items = (
            EsSessionStart.objects
            .prefetch_related("essessionstartday_set")
            .filter(exam_period_id=request.data["exam_period_id"])
            .all()
        )
        items = self.session_start_listing_filter(request, items)
        pagination = self.session_start_listing_pagination(request, items)

        return_data=[]

        for val in pagination["paginated_data"]:
            data={}
            data["id"] = val.id
            data["code"] = val.code
            data["start_time"] = val.start_time
            data["status"] = val.status
            data["status_text"] = __("attr.session_start_status." + str(val.status))
            days = [
                {
                    "id": rp.day,
                    "name": __("attr.days_name." + str(rp.day))
                }
                for rp in val.essessionstartday_set.order_by("day").all()
            ]
            data['days'] = days

            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 session_start_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":
                    model = model.filter(code__icontains=val)
                case "start_time_from":
                    try:
                        model = model.filter(start_time__gte=datetime.strptime(val, "%H:%M:%S").time())
                    except (TypeError, ValueError):
                        try:
                            model = model.filter(start_time__gte=datetime.strptime(val, "%H:%M").time())
                        except (TypeError, ValueError):
                            continue
                case "start_time_to":
                    try:
                        model = model.filter(start_time__lte=datetime.strptime(val, "%H:%M:%S").time())
                    except (TypeError, ValueError):
                        try:
                            model = model.filter(start_time__lte=datetime.strptime(val, "%H:%M").time())
                        except (TypeError, ValueError):
                            continue
                case "days":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(essessionstartday__day__in=list(val))
                    else:
                        model = model.filter(essessionstartday__day=val)
                case "status":
                    if isinstance(val, (list, tuple, set)):
                        model = model.filter(status__in=list(val))
                    else:
                        model = model.filter(status=val)
                case _:
                    continue

        return model.distinct()

    def session_start_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 sort_by == "days":
                from django.db.models import Min
                model = model.annotate(_first_day=Min("essessionstartday__day"))
                sort_col = "_first_day"
            else:
                sort_col = sort_by

            if str(order_by).lower() == "desc":
                ordering_string = f"-{sort_col}"
            else:
                ordering_string = sort_col
            model = model.order_by(ordering_string)
        else:
            model = model.order_by("-id")

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