from rest_framework import status
from rest_framework.exceptions import ValidationError

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

        session_ids = request.data.get("session_id")

        # Fetch all sessions with their locations (preserving input order)
        sessions_map = {
            s.id: s for s in EsSession.objects.select_related("location").filter(id__in=session_ids)
        }

        # Fetch all seats grouped by session
        seats = EsSessionSeat.objects.select_related(
            "student", "session"
        ).filter(session_id__in=session_ids).order_by("session_id", "seat_number")

        # Group seats by session_id
        seats_by_session = {}
        for seat in seats:
            seats_by_session.setdefault(seat.session_id, []).append(seat)

        # Get all activities for these sessions to map exam requirements to sessions
        activities = EsExamActivity.objects.filter(session_id__in=session_ids).select_related("exam_requirement")
        activities_by_session = {}
        for act in activities:
            activities_by_session.setdefault(act.session_id, []).append(act)

        # Get all exam requirements involved
        requirement_ids = {act.exam_requirement_id for act in activities if act.exam_requirement_id}

        # Fetch all EsExamRequirementStudent records for these requirements to map students
        req_students = EsExamRequirementStudent.objects.filter(
            exam_requirement_id__in=requirement_ids, 
            student_id__isnull=False
        ).select_related("exam_requirement")
        
        student_req_map = {}
        for rs in req_students:
            if rs.exam_requirement:
                student_req_map.setdefault(rs.student_id, []).append((rs.exam_requirement_id, rs.exam_requirement.code))

        # Build per-session response
        sessions_data = []
        for sid in session_ids:
            session = sessions_map.get(sid)
            if not session:
                continue

            location = session.location

            # Build location info
            if location and location.row and location.column:
                unavail_qs = EsLocationUnavailableSeat.objects.filter(
                    location_id=location.id
                ).order_by("cord_x", "cord_y")
                unavail_count = unavail_qs.count()
                total = location.row * location.column
                available = max(0, total - unavail_count)
                unavailable_seats = [
                    {
                        "type": u.type,
                        "type_text": __(f"attr.location_unavailable_seat_type.{u.type}"),
                        "x": u.cord_x,
                        "y": u.cord_y,
                    }
                    for u in unavail_qs
                ]
            else:
                total = None
                available = None
                unavailable_seats = []

            seat_order_val = location.seat_order if location else None

            location_info = {
                "location_name": location.name if location else None,
                "row": location.row if location else None,
                "column": location.column if location else None,
                "seat_order": seat_order_val,
                "seat_order_text": __(f"attr.location_seat_order.{seat_order_val}") if seat_order_val else None,
                "location_total_seats": total,
                "location_available_seats": available,
                "unavailable_seats": unavailable_seats,
            }

            # Group student_list by exam_requirement_code
            session_activities = activities_by_session.get(sid, [])
            seen_reqs = {}
            for act in session_activities:
                req = act.exam_requirement
                if req and req.id not in seen_reqs:
                    seen_reqs[req.id] = req

            seats_in_session = seats_by_session.get(sid, [])
            session_req_ids = {req_id for req_id in seen_reqs.keys()}
            
            student_list_grouped = []
            for req_id, req in seen_reqs.items():
                req_student_list = []
                for seat in seats_in_session:
                    student_req_ids = [item[0] for item in student_req_map.get(seat.student_id, [])]
                    if req_id in student_req_ids:
                        req_student_list.append({
                            "student_name": seat.student.name if seat.student else None,
                        })
                student_list_grouped.append({
                    "exam_requirement_code": req.code,
                    "student_list": req_student_list,
                })

            flat_seat_list = []
            for seat in seats_in_session:
                req_code = None
                for req_id, code in student_req_map.get(seat.student_id, []):
                    if req_id in session_req_ids:
                        req_code = code
                        break
                flat_seat_list.append({
                    "student_id": seat.student_id,
                    "student_name": seat.student.name if seat.student else None,
                    "exam_requirement_code": req_code,
                    "seat_number": seat.seat_number,
                })

            sessions_data.append({
                "session_id": session.id,
                "session_name": session.name,
                "student_list": student_list_grouped,
                "location_details": location_info,
                "seat_list": flat_seat_list,
            })

        return {"sessions": sessions_data}
