from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models.es_session import EsSession
from api.models.es_exam_activity import EsExamActivity
from api.models.es_exam_requirement_student import EsExamRequirementStudent
from api.models.es_location_unavailable_seat import EsLocationUnavailableSeat
from api.models.es_session_seat import EsSessionSeat
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 SessionSeatAssign(AdminApiBase):
    def post(self, request):
        try:
            result = self.get_data(request)
            return self.api_response(data={"list": result})
        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):
        # Validate session_id using BaseValidator
        rules = {
            "session_id": "required|integer|exists:api.EsSession,id",
            "seat_order": "required|in:1,2",
        }
        attribute = {
            "session_id": __("attr.session_id"),
            "seat_order": __("attr.seat_order"),
        }
        validator = BaseValidator(request.data, rules, attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

        session_id = int(request.data.get("session_id"))
        seat_order = request.data.get("seat_order")

        session = EsSession.objects.select_related("location").filter(id=session_id).first()
        if not session:
            raise ValidationError({"error": __("message.session_not_found"), "errors": {}})

        location = session.location
        if not location:
            raise ValidationError({"error": __("message.location_not_set_for_session"), "errors": {}})

        # Fetch activities for the session
        activities = EsExamActivity.objects.filter(session_id=session.id)
        requirement_ids = [activity.exam_requirement_id for activity in activities]

        # Fetch students
        requirement_students = EsExamRequirementStudent.objects.filter(
            exam_requirement_id__in=requirement_ids, 
            student_id__isnull=False
        ).select_related("student").all()
        
        # Deduplicate students just in case a student is in multiple requirements for the same session
        unique_students = {}
        for req_stud in requirement_students:
            if req_stud.student:
                unique_students[req_stud.student.id] = req_stud.student
        
        students = list(unique_students.values())
        students.sort(key=lambda s: s.code)

        if not students:
            # If no students, clear seats and return empty list
            EsSessionSeat.objects.filter(session_id=session.id).delete()
            return []

        # Calculate total available capacity
        total_seats = (location.row or 0) * (location.column or 0)
        unavailable_count = EsLocationUnavailableSeat.objects.filter(location_id=location.id).count()
        available_capacity = total_seats - unavailable_count

        if len(students) > available_capacity:
            raise ValidationError({"error": __("message.not_enough_seats_available"), "errors": {}})

        # Persist the chosen seat_order to the location
        location.seat_order = int(seat_order)
        location.save(update_fields=["seat_order"])

        EsSessionSeat.objects.filter(session_id=session.id).delete()

        new_seats = []
        for i, student in enumerate(students):
            new_seats.append(
                EsSessionSeat(
                    session_id=session.id,
                    student_id=student.id,
                    seat_number=i + 1,  # always sequential: 1, 2, 3...
                    created_by=request.user.id if hasattr(request, "user") and request.user else None
                )
            )

        EsSessionSeat.objects.bulk_create(new_seats)

        # Build return data
        response_data = []
        for i, seat in enumerate(new_seats):
            student = students[i]
            response_data.append({
                "student_id": seat.student_id,
                "student_code": student.code,
                "student_name": student.name,
                "seat_number": seat.seat_number,
            })

        return response_data
