from rest_framework import viewsets, permissions, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from wsgiref.util import FileWrapper
from django.db.models import Count, Q
from datetime import date
from django.db import transaction
import logging
import os
from django.http import HttpRequest, HttpResponse
import sys

logger = logging.getLogger(__name__)
# from django_filters.rest_framework import DjangoFilterBackend
from .models import (
    AcademicYear,
    AcademicClass,
    Standard,
    Section,
    Subject,
    AcademicTerm,
    ClassSubject,
    SubjectCategory,
    SubjectGroup,
    ClassSubject,
    StudentSubjectGroup,
    StudentEnrollment,
    StudentSubject,
)
from people.models import Teacher, Student
from people.serializers import TeacherSerializer
from .serializers import (
    AcademicYearSerializer,
    AcademicClassSerializer,
    StandardSerializer,
    SectionSerializer,
    SubjectSerializer,
    AcademicTermSerializer,
    ClassSubjectSerializer,
    SubjectCategorySerializer,
    SubjectGroupSerializer,
    ClassSubjectSerializer,
    ClassSubjectCreateSerializer,
    ClassSubjectBulkSerializer,
    StudentSubjectGroupCreateUpdateSerializer,
    SimpleStudentSerializer,
    TransferToNextYearSerializer,
    SimpleSubjectGroupSerializer,
    StudentSubjectGroupSerializer,
    StudentEnrollmentSerializer,
    StudentEnrollmentCreateSerializer,
    StudentSubjectCreateSerializer,
    StudentEnrollmentBulkUpdateSerializer,
    StudentSubjectGroupBulkUpdateSerializer,
    StudentSubjectGroupBulkCreateSerializer,
    StudentSubjectSerializer,
    StudentSubjectBulkCreateSerializer,
    StudentSubjectBulkUpdateSerializer,
    SimpleStudentSubjectSerializer,
    SimpleSubjectTeacherSerializer,
    SubjectTeacherBulkCreateSerializer,
    SubjectTeacherCreateUpdateSerializer,
    SubjectTeacherSerializer,
)


class AcademicYearViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing academic years.
    """

    queryset = AcademicYear.objects.all()
    serializer_class = AcademicYearSerializer
    permission_classes = [permissions.IsAuthenticated]

    search_fields = ["name", "code"]
    ordering_fields = ["start_date", "end_date", "name", "is_active"]
    ordering = ["-start_date"]

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        queryset = AcademicYear.objects.all()

        status_filter = self.request.query_params.get("filter", None)
        today = date.today()

        if status_filter == "active":
            queryset = queryset.filter(is_active=True)
        elif status_filter == "past":
            queryset = queryset.filter(end_date__lt=today)
        elif status_filter == "future":
            queryset = queryset.filter(start_date__gt=today)
        elif status_filter == "current":
            queryset = queryset.filter(start_date__lte=today, end_date__gte=today)

        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search) | Q(code__icontains=search)
            )

        return queryset

    # ── NEW: aggregate counts for the stats cards ──────────────────────────────
    @action(detail=False, methods=["get"], url_path="summary")
    def summary(self, request):
        """
        Returns total/active/current/upcoming/past counts across ALL academic
        years, ignoring filter/search params so the stats cards always show
        real totals. Called by the frontend on load and after every mutation.
        """
        today = date.today()
        qs = AcademicYear.objects.all()
        return Response(
            {
                "total": qs.count(),
                "active": qs.filter(is_active=True).count(),
                "current": qs.filter(
                    start_date__lte=today, end_date__gte=today
                ).count(),
                "upcoming": qs.filter(start_date__gt=today).count(),
                "past": qs.filter(end_date__lt=today).count(),
            },
            status=status.HTTP_200_OK,
        )

    @action(detail=True, methods=["post"])
    def set_active(self, request, pk=None):
        """
        Set this academic year as active and deactivate others.
        """
        if not request.user.is_staff:
            return Response(
                {"error": "Only admin users can set active academic year."},
                status=status.HTTP_403_FORBIDDEN,
            )

        try:
            academic_year = self.get_object()
            AcademicYear.objects.exclude(pk=academic_year.pk).update(is_active=False)
            academic_year.is_active = True
            academic_year.save()
            return Response(
                {"message": f"{academic_year.name} is now the active academic year."},
                status=status.HTTP_200_OK,
            )
        except AcademicYear.DoesNotExist:
            return Response(
                {"error": "Academic year not found."}, status=status.HTTP_404_NOT_FOUND
            )

    @action(detail=False, methods=["get"])
    def current(self, request):
        """
        Get the current academic year (where today falls within start and end dates).
        """
        today = date.today()
        try:
            current_year = AcademicYear.objects.filter(
                start_date__lte=today, end_date__gte=today
            ).first()

            if current_year:
                serializer = self.get_serializer(current_year)
                return Response(serializer.data)
            else:
                return Response(
                    {"error": "No current academic year found."},
                    status=status.HTTP_404_NOT_FOUND,
                )
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=True, methods=["get"])
    def statistics(self, request, pk=None):
        """
        Get statistics for a specific academic year.
        """
        try:
            academic_year = self.get_object()
            stats = {
                "total_classes": AcademicClass.objects.filter(
                    academic_year=academic_year
                ).count(),
                "active_classes": AcademicClass.objects.filter(
                    academic_year=academic_year, is_active=True
                ).count(),
                "total_students": 0,
                "total_subjects": Subject.objects.count(),
                "terms": AcademicTerm.objects.filter(
                    academic_year=academic_year
                ).count(),
            }
            return Response(stats)
        except AcademicYear.DoesNotExist:
            return Response(
                {"error": "Academic year not found."}, status=status.HTTP_404_NOT_FOUND
            )


class AcademicClassViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing academic classes.
    """

    queryset = AcademicClass.objects.all()
    serializer_class = AcademicClassSerializer
    permission_classes = [permissions.IsAuthenticated]
    # filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    filterset_fields = ["academic_year", "standard", "section", "is_active"]
    search_fields = ["room_number", "standard__name", "section__code"]

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        """
        Custom queryset to prefetch related data for better performance
        """
        queryset = AcademicClass.objects.select_related(
            "academic_year", "standard", "section", "class_teacher", "assistant_teacher"
        ).all()

        # Filter by academic year if provided
        academic_year_id = self.request.query_params.get("academic_year", None)
        if academic_year_id and academic_year_id != "all":
            queryset = queryset.filter(academic_year_id=academic_year_id)

        # Filter by standard if provided
        standard_id = self.request.query_params.get("standard", None)
        if standard_id and standard_id != "all":
            queryset = queryset.filter(standard_id=standard_id)

        # Filter by active status if provided
        is_active = self.request.query_params.get("is_active", None)
        if is_active and is_active != "all":
            queryset = queryset.filter(
                is_active=(is_active == "true" or is_active == "active")
            )

        return queryset


class StandardViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing standards.
    """

    queryset = Standard.objects.all()
    serializer_class = StandardSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name", "code"]

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]


class SectionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing sections.
    """

    queryset = Section.objects.all()
    serializer_class = SectionSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]


class SubjectViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing subjects.
    """

    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer
    permission_classes = [permissions.IsAuthenticated]

    filterset_fields = [
        "standard",
        "category",
        "subject_group",
        "subject_type",
        "is_active",
    ]
    search_fields = ["name", "code", "standard__name", "category__name"]
    ordering_fields = ["name", "code", "standard__order"]
    ordering = ["standard__order", "name"]

    def get_permissions(self):
        """
        Customize permissions based on action.
        """
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        """
        Optionally restricts the returned subjects,
        by filtering against query parameters in the URL.
        """
        queryset = Subject.objects.select_related(
            "standard", "category", "subject_group"
        ).all()

        # Filter by standard
        standard_id = self.request.query_params.get("standard", None)
        if standard_id and standard_id != "all":
            queryset = queryset.filter(standard_id=standard_id)

        # Filter by category
        category_id = self.request.query_params.get("category", None)
        if category_id and category_id != "all":
            queryset = queryset.filter(category_id=category_id)

        # Filter by subject type
        subject_type = self.request.query_params.get("subject_type", None)
        if subject_type and subject_type != "all":
            queryset = queryset.filter(subject_type=subject_type)

        # Filter by status
        is_active = self.request.query_params.get("is_active", None)
        if is_active and is_active != "all":
            queryset = queryset.filter(
                is_active=(is_active.lower() == "true" or is_active == "active")
            )

        # Search
        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search)
                | Q(code__icontains=search)
                | Q(standard__name__icontains=search)
                | Q(category__name__icontains=search)
            )

        return queryset

    def create(self, request, *args, **kwargs):
        """
        Create a new subject with validation.
        """
        # Validate standard exists
        standard_id = request.data.get("standard")
        try:
            standard = Standard.objects.get(id=standard_id)
        except Standard.DoesNotExist:
            return Response(
                {"standard": ["Standard not found."]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Validate category exists
        category_id = request.data.get("category")
        try:
            category = SubjectCategory.objects.get(id=category_id)
        except SubjectCategory.DoesNotExist:
            return Response(
                {"category": ["Category not found."]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Validate subject_group if provided
        subject_group_id = request.data.get("subject_group")
        if subject_group_id:
            try:
                subject_group = SubjectGroup.objects.get(id=subject_group_id)
                if subject_group.standard != standard:
                    return Response(
                        {
                            "subject_group": [
                                "Subject group does not belong to the selected standard."
                            ]
                        },
                        status=status.HTTP_400_BAD_REQUEST,
                    )
            except SubjectGroup.DoesNotExist:
                return Response(
                    {"subject_group": ["Subject group not found."]},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for subjects.
        """
        try:
            total_subjects = Subject.objects.count()

            stats = {
                "total": total_subjects,
                "active": Subject.objects.filter(is_active=True).count(),
                "inactive": Subject.objects.filter(is_active=False).count(),
                "by_type": {
                    "mandatory": Subject.objects.filter(
                        subject_type="mandatory"
                    ).count(),
                    "elective": Subject.objects.filter(subject_type="elective").count(),
                    "language": Subject.objects.filter(subject_type="language").count(),
                },
                "by_category": {},
                "by_standard": {},
            }

            # Count by category
            categories = SubjectCategory.objects.all()
            for category in categories:
                count = Subject.objects.filter(category=category).count()
                if count > 0:
                    stats["by_category"][category.name] = count

            # Count by standard
            standards = Standard.objects.all()
            for standard in standards:
                count = Subject.objects.filter(standard=standard).count()
                if count > 0:
                    stats["by_standard"][standard.name] = count

            return Response(stats)
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def dropdown(self, request):
        """
        Get simplified list for dropdown selection.
        """
        standard_id = request.query_params.get("standard", None)
        subject_group_id = request.query_params.get("subject_group", None)

        subjects = Subject.objects.filter(is_active=True)

        if standard_id:
            subjects = subjects.filter(standard_id=standard_id)

        if subject_group_id:
            subjects = subjects.filter(subject_group_id=subject_group_id)

        subjects = subjects.select_related("standard", "category").order_by(
            "standard__order", "name"
        )

        subject_data = []
        for subject in subjects:
            subject_data.append(
                {
                    "id": subject.id,
                    "name": subject.name,
                    "code": subject.code,
                    "standard_id": subject.standard.id,
                    "standard_name": subject.standard.name,
                    "category_id": subject.category.id,
                    "category_name": subject.category.name,
                    "subject_type": subject.subject_type,
                    "display_name": f"{subject.name} ({subject.code}) - {subject.standard.name}",
                }
            )

        return Response(subject_data)

    @action(detail=True, methods=["get"])
    def usage(self, request, pk=None):
        """
        Get usage statistics for a specific subject.
        """
        try:
            subject = self.get_object()

            # Count classes using this subject
            # You'll need to implement this based on your ClassSubject model
            # For example:
            # class_count = ClassSubject.objects.filter(subject=subject).count()

            usage_stats = {
                "subject_id": subject.id,
                "subject_name": subject.name,
                "subject_code": subject.code,
                "standard_name": subject.standard.name,
                "category_name": subject.category.name,
                "class_count": 0,  # Placeholder
                "teacher_count": 0,  # Placeholder
                "is_in_use": False,  # Placeholder
            }

            return Response(usage_stats)
        except Subject.DoesNotExist:
            return Response(
                {"error": "Subject not found."}, status=status.HTTP_404_NOT_FOUND
            )

        # Add this new action for downloading documents

    @action(detail=True, methods=["get"], url_path="download")
    def download_document(self, request, pk=None):
        """
        Download the book document for a subject.
        """
        subject = self.get_object()

        if not subject.book_document:
            return Response(
                {"error": "No document found for this subject."},
                status=status.HTTP_404_NOT_FOUND,
            )

        try:
            # Get the file path
            file_path = subject.book_document.path

            # Open the file
            with open(file_path, "rb") as file:
                response = HttpResponse(
                    FileWrapper(file), content_type="application/octet-stream"
                )

                # Get filename from the document
                filename = os.path.basename(subject.book_document.name)

                # Set headers for download
                response["Content-Disposition"] = f'attachment; filename="{filename}"'
                response["Content-Length"] = os.path.getsize(file_path)

                return response

        except Exception as e:
            return Response(
                {"error": f"Failed to download document: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    # Optional: Add an action to view document info without downloading
    @action(detail=True, methods=["get"], url_path="document-info")
    def document_info(self, request, pk=None):
        """
        Get information about the book document.
        """
        subject = self.get_object()

        if not subject.book_document:
            return Response({"has_document": False}, status=status.HTTP_200_OK)

        # Get file information
        file_name = os.path.basename(subject.book_document.name)
        file_size = subject.book_document.size
        file_extension = os.path.splitext(file_name)[1].lower()

        # Determine file type
        file_type = "unknown"
        if file_extension == ".pdf":
            file_type = "PDF Document"
        elif file_extension in [".doc", ".docx"]:
            file_type = "Word Document"
        elif file_extension == ".txt":
            file_type = "Text File"
        elif file_extension == ".epub":
            file_type = "EPUB Book"

        return Response(
            {
                "has_document": True,
                "filename": file_name,
                "file_size": file_size,
                "file_size_mb": round(file_size / (1024 * 1024), 2),
                "file_type": file_type,
                "file_extension": file_extension,
                "uploaded_at": subject.book_document.field.upload_to,
            }
        )

    @action(detail=False, methods=["get"], url_path="student-books")
    def student_books(self, request):
        """
        GET /academics/subjects/student-books/?student_id=<id>

        Returns all subjects (with book info) applicable to a student:
        - Mandatory subjects come from the class the student is enrolled in.
        - Elective/language subjects are filtered to only those the student
          has been specifically assigned (StudentSubject).
        """
        student_id = request.query_params.get("student_id")
        if not student_id:
            return Response(
                {"error": "student_id parameter is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            student = Student.objects.get(id=student_id)
        except Student.DoesNotExist:
            return Response(
                {"error": "Student not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        enrollment = (
            StudentEnrollment.objects.filter(student=student, is_active=True)
            .select_related(
                "academic_class",
                "academic_class__standard",
                "academic_class__section",
                "academic_class__academic_year",
            )
            .first()
        )

        if not enrollment:
            return Response(
                {"error": "No active enrollment found for this student."},
                status=status.HTTP_404_NOT_FOUND,
            )

        academic_class = enrollment.academic_class

        student_specific_subject_ids = set(
            StudentSubject.objects.filter(
                enrollment=enrollment, is_active=True
            ).values_list("subject_id", flat=True)
        )

        class_subjects = ClassSubject.objects.filter(
            academic_class=academic_class
        ).select_related("subject", "subject__category", "subject__subject_group")

        subjects_data = []
        for cs in class_subjects:
            subj = cs.subject
            if subj.subject_type in ["elective", "language"] and subj.id not in student_specific_subject_ids:
                continue

            book_url = None
            book_name = None
            if subj.book_document:
                book_url = request.build_absolute_uri(subj.book_document.url)
                book_name = os.path.basename(subj.book_document.name)

            subjects_data.append(
                {
                    "subject_id": subj.id,
                    "subject_name": subj.name,
                    "subject_code": subj.code,
                    "subject_type": subj.subject_type,
                    "is_student_specific": subj.id in student_specific_subject_ids,
                    "category": (
                        {
                            "id": subj.category.id,
                            "name": subj.category.name,
                            "code": subj.category.code,
                        }
                        if subj.category
                        else None
                    ),
                    "subject_group": (
                        {
                            "id": subj.subject_group.id,
                            "name": subj.subject_group.name,
                        }
                        if subj.subject_group
                        else None
                    ),
                    "has_book": bool(subj.book_document),
                    "book_document_url": book_url,
                    "book_document_name": book_name,
                }
            )

        return Response(
            {
                "student_id": student.id,
                "student_name": f"{student.first_name or ''} {student.last_name or ''}".strip(),
                "admission_number": student.admission_number or "",
                "enrollment_id": enrollment.id,
                "class": {
                    "id": academic_class.id,
                    "name": f"{academic_class.standard.name} - {academic_class.section.name if academic_class.section else 'All'}",
                    "standard": {
                        "id": academic_class.standard.id,
                        "name": academic_class.standard.name,
                    },
                    "academic_year": (
                        academic_class.academic_year.name
                        if academic_class.academic_year
                        else None
                    ),
                },
                "total_subjects": len(subjects_data),
                "subjects_with_books": sum(1 for s in subjects_data if s["has_book"]),
                "subjects": subjects_data,
            }
        )

    def destroy(self, request, *args, **kwargs):
        """
        Delete a subject and its associated book document file.
        """
        try:
            instance = self.get_object()
            
            # Delete the book document file if it exists
            if instance.book_document:
                try:
                    # Check if file exists before attempting to delete
                    if os.path.exists(instance.book_document.path):
                        # Delete the file from storage
                        instance.book_document.delete(save=False)
                        logger.info(f"Deleted book document for subject: {instance.name}")
                except Exception as e:
                    logger.error(f"Error deleting document for subject {instance.id}: {str(e)}")
                    # Continue with deletion even if file deletion fails
            
            # Delete the subject from database
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
            
        except ObjectDoesNotExist:
            return Response(
                {"error": "Subject not found."},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"Error deleting subject: {str(e)}")
            return Response(
                {"error": f"Failed to delete subject: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

class AcademicTermViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing academic terms.
    """

    queryset = AcademicTerm.objects.all()
    serializer_class = AcademicTermSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        queryset = AcademicTerm.objects.all()
        academic_year_id = self.request.query_params.get("academic_year", None)
        if academic_year_id:
            queryset = queryset.filter(academic_year_id=academic_year_id)
        return queryset

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]


class ClassSubjectViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing class subjects.
    """

    queryset = ClassSubject.objects.all()
    serializer_class = ClassSubjectSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        queryset = ClassSubject.objects.all()
        academic_class_id = self.request.query_params.get("academic_class", None)
        if academic_class_id:
            queryset = queryset.filter(academic_class_id=academic_class_id)
        return queryset

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

class TeacherViewSet(viewsets.ModelViewSet):
    queryset = Teacher.objects.filter(is_active=True)
    serializer_class = TeacherSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["first_name", "last_name", "email", "employee_id"]

    def get_permissions(self):
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        queryset = Teacher.objects.filter(is_active=True)
        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(first_name__icontains=search)
                | Q(last_name__icontains=search)
                | Q(email__icontains=search)
                | Q(employee_id__icontains=search)
            )
        return queryset.order_by("first_name", "last_name")

    @action(detail=False, methods=["get"], url_path="list-simple")
    def list_simple(self, request):
        queryset = Teacher.objects.filter(is_active=True)

        search = request.query_params.get("search", "").strip()
        if search:
            queryset = queryset.filter(
                Q(first_name__icontains=search)
                | Q(last_name__icontains=search)
                | Q(email__icontains=search)
                | Q(employee_id__icontains=search)
            )
            # If searching, return all matches (but limit to 50 to avoid overload)
            queryset = queryset.order_by("first_name", "last_name")[:50]
        else:
            # Initial load - only 15 records
            queryset = queryset.order_by("first_name", "last_name")[:15]

        teacher_data = []
        for teacher in queryset:
            teacher_data.append({
                "id": teacher.id,
                "first_name": teacher.first_name or "",
                "last_name": teacher.last_name or "",
                "email": teacher.email or "",
                "employee_id": teacher.employee_id or "",
                "full_name": teacher.full_name
                or f"{teacher.first_name or ''} {teacher.last_name or ''}".strip(),
                "phone": teacher.phone or "",
                "profile_image": (
                    request.build_absolute_uri(teacher.profile_image.url)
                    if teacher.profile_image
                    else None
                ),
                "designation": teacher.employment_type or "",
                "qualification": teacher.qualification or "",
            })

        return Response(teacher_data)

class SubjectCategoryViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing subject categories.
    """

    queryset = SubjectCategory.objects.all()
    serializer_class = SubjectCategorySerializer
    permission_classes = [permissions.IsAuthenticated]

    search_fields = ["name", "code"]
    ordering_fields = ["name", "code", "id"]
    ordering = ["name"]

    def get_permissions(self):
        """
        Customize permissions based on action.
        """
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        """
        Optionally restricts the returned categories,
        by filtering against query parameters in the URL.
        """
        queryset = SubjectCategory.objects.all()

        # Search
        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search) | Q(code__icontains=search)
            )

        return queryset

    def create(self, request, *args, **kwargs):
        """
        Create a new subject category with validation.
        """
        # Check if category with same name or code already exists
        name = request.data.get("name", "").strip()
        code = request.data.get("code", "").strip().upper()

        if SubjectCategory.objects.filter(name__iexact=name).exists():
            return Response(
                {"name": ["A category with this name already exists."]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if SubjectCategory.objects.filter(code__iexact=code).exists():
            return Response(
                {"code": ["A category with this code already exists."]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return super().create(request, *args, **kwargs)

    def update(self, request, *args, **kwargs):
        """
        Update a subject category with validation.
        """
        instance = self.get_object()
        name = request.data.get("name", "").strip()
        code = request.data.get("code", "").strip().upper()

        # Check if another category has the same name
        if name and name.lower() != instance.name.lower():
            if (
                SubjectCategory.objects.filter(name__iexact=name)
                .exclude(pk=instance.pk)
                .exists()
            ):
                return Response(
                    {"name": ["Another category with this name already exists."]},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        # Check if another category has the same code
        if code and code.upper() != instance.code.upper():
            if (
                SubjectCategory.objects.filter(code__iexact=code)
                .exclude(pk=instance.pk)
                .exists()
            ):
                return Response(
                    {"code": ["Another category with this code already exists."]},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return super().update(request, *args, **kwargs)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for subject categories.
        """
        try:
            total_categories = SubjectCategory.objects.count()

            # Count by common categories
            stats = {
                "total": total_categories,
                "core_count": SubjectCategory.objects.filter(code="CORE").count(),
                "language_count": SubjectCategory.objects.filter(
                    code="LANGUAGE"
                ).count(),
                "elective_count": SubjectCategory.objects.filter(
                    code="ELECTIVE"
                ).count(),
                "optional_count": SubjectCategory.objects.filter(
                    code="OPTIONAL"
                ).count(),
                "other_count": SubjectCategory.objects.exclude(
                    code__in=["CORE", "LANGUAGE", "ELECTIVE", "OPTIONAL"]
                ).count(),
            }

            return Response(stats)
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def dropdown(self, request):
        """
        Get simplified list for dropdown selection.
        """
        categories = SubjectCategory.objects.all().order_by("name")

        category_data = []
        for category in categories:
            category_data.append(
                {
                    "id": category.id,
                    "name": category.name,
                    "code": category.code,
                    "display_name": f"{category.name} ({category.code})",
                }
            )

        return Response(category_data)

    @action(detail=True, methods=["get"])
    def usage(self, request, pk=None):
        """
        Get usage statistics for a specific category.
        """
        try:
            category = self.get_object()

            # Count subjects using this category
            # You'll need to implement this based on your Subject model
            # For example:
            # subject_count = Subject.objects.filter(category=category).count()

            usage_stats = {
                "category_id": category.id,
                "category_name": category.name,
                "category_code": category.code,
                "subject_count": 0,  # Placeholder - implement based on your models
                "class_count": 0,  # Placeholder - implement based on your models
                "is_in_use": False,  # Placeholder
            }

            return Response(usage_stats)
        except SubjectCategory.DoesNotExist:
            return Response(
                {"error": "Subject category not found."},
                status=status.HTTP_404_NOT_FOUND,
            )


class SubjectGroupViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing subject groups.
    """

    queryset = SubjectGroup.objects.all()
    serializer_class = SubjectGroupSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = ["standard", "is_active"]
    search_fields = ["name", "code", "standard__name"]
    ordering_fields = ["name", "code", "standard__order"]
    ordering = ["standard__order", "name"]

    def get_permissions(self):
        """
        Customize permissions based on action.
        """
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        """
        Optionally restricts the returned groups,
        by filtering against query parameters in the URL.
        """
        queryset = SubjectGroup.objects.select_related("standard").all()

        # Filter by standard
        standard_id = self.request.query_params.get("standard", None)
        if standard_id and standard_id != "all":
            queryset = queryset.filter(standard_id=standard_id)

        # Filter by status
        is_active = self.request.query_params.get("is_active", None)
        if is_active and is_active != "all":
            queryset = queryset.filter(
                is_active=(is_active.lower() == "true" or is_active == "active")
            )

        # Search
        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search)
                | Q(code__icontains=search)
                | Q(standard__name__icontains=search)
            )

        return queryset

    def create(self, request, *args, **kwargs):
        """
        Create a new subject group with validation.
        """
        # Validate standard exists
        standard_id = request.data.get("standard")
        try:
            standard = Standard.objects.get(id=standard_id)
            if standard.standard_type != "higher_secondary":
                return Response(
                    {
                        "standard": [
                            "Subject groups can only be created for higher secondary standards."
                        ]
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
        except Standard.DoesNotExist:
            return Response(
                {"standard": ["Standard not found."]},
                status=status.HTTP_400_BAD_REQUEST,
            )

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for subject groups.
        """
        try:
            total_groups = SubjectGroup.objects.count()

            stats = {
                "total": total_groups,
                "active": SubjectGroup.objects.filter(is_active=True).count(),
                "inactive": SubjectGroup.objects.filter(is_active=False).count(),
                "by_standard": {},
            }

            # Count by standard
            standards = Standard.objects.filter(standard_type="higher_secondary")
            for standard in standards:
                count = SubjectGroup.objects.filter(standard=standard).count()
                if count > 0:
                    stats["by_standard"][standard.name] = count

            return Response(stats)
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def dropdown(self, request):
        """
        Get simplified list for dropdown selection.
        """
        standard_id = request.query_params.get("standard", None)

        groups = SubjectGroup.objects.filter(is_active=True)
        if standard_id:
            groups = groups.filter(standard_id=standard_id)

        groups = groups.select_related("standard").order_by("standard__order", "name")

        group_data = []
        for group in groups:
            group_data.append(
                {
                    "id": group.id,
                    "name": group.name,
                    "code": group.code,
                    "standard_id": group.standard.id,
                    "standard_name": group.standard.name,
                    "display_name": f"{group.name} ({group.code}) - {group.standard.name}",
                }
            )

        return Response(group_data)

    @action(detail=True, methods=["get"])
    def usage(self, request, pk=None):
        """
        Get usage statistics for a specific group.
        """
        try:
            group = self.get_object()

            # Count students using this group
            # You'll need to implement this based on your Student model
            # For example:
            # student_count = Student.objects.filter(subject_group=group).count()

            usage_stats = {
                "group_id": group.id,
                "group_name": group.name,
                "group_code": group.code,
                "standard_name": group.standard.name,
                "student_count": 0,  # Placeholder - implement based on your models
                "class_count": 0,  # Placeholder - implement based on your models
                "is_in_use": False,  # Placeholder
            }

            return Response(usage_stats)
        except SubjectGroup.DoesNotExist:
            return Response(
                {"error": "Subject group not found."}, status=status.HTTP_404_NOT_FOUND
            )


class ClassSubjectViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing class subjects.
    """

    queryset = ClassSubject.objects.all()
    permission_classes = [permissions.IsAuthenticated]
    # filter_backends = [
    #     DjangoFilterBackend,
    #     filters.SearchFilter,
    #     filters.OrderingFilter,
    # ]
    filterset_fields = ["academic_class", "subject", "academic_class__academic_year"]
    search_fields = [
        "academic_class__standard__name",
        "academic_class__section__code",
        "academic_class__academic_year__name",
        "subject__name",
        "subject__code",
    ]
    ordering_fields = [
        "academic_class__standard__order",
        "academic_class__section__order",
        "subject__name",
        "created_at",
    ]
    ordering = [
        "academic_class__standard__order",
        "academic_class__section__order",
        "subject__name",
    ]

    def get_serializer_class(self):
        """
        Use different serializers for different actions.
        """
        if self.action == "create":
            return ClassSubjectCreateSerializer
        if self.action == "bulk_assign":
            return ClassSubjectBulkSerializer
        return ClassSubjectSerializer

    def get_permissions(self):
        """
        Customize permissions based on action.
        """
        if self.action in [
            "create",
            "update",
            "partial_update",
            "destroy",
            "bulk_assign",
        ]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        """
        Optionally restricts the returned class subjects,
        by filtering against query parameters in the URL.
        """
        queryset = ClassSubject.objects.select_related(
            "academic_class",
            "academic_class__standard",
            "academic_class__section",
            "academic_class__academic_year",
            "subject",
            "subject__category",
            "subject__subject_group",
        ).all()

        # Filter by academic class
        academic_class_id = self.request.query_params.get("academic_class", None)
        if academic_class_id and academic_class_id != "all":
            queryset = queryset.filter(academic_class_id=academic_class_id)

        # Filter by subject
        subject_id = self.request.query_params.get("subject", None)
        if subject_id and subject_id != "all":
            queryset = queryset.filter(subject_id=subject_id)

        # Filter by academic year
        academic_year = self.request.query_params.get("academic_year", None)
        if academic_year and academic_year != "all":
            queryset = queryset.filter(
                academic_class__academic_year__name=academic_year
            )

        # Filter by standard
        standard_id = self.request.query_params.get("standard", None)
        if standard_id and standard_id != "all":
            queryset = queryset.filter(academic_class__standard_id=standard_id)

        # Filter by category
        category_id = self.request.query_params.get("category", None)
        if category_id and category_id != "all":
            queryset = queryset.filter(subject__category_id=category_id)

        # Search
        search = self.request.query_params.get("search", None)
        if search:
            queryset = queryset.filter(
                Q(academic_class__standard__name__icontains=search)
                | Q(academic_class__section__code__icontains=search)
                | Q(academic_class__academic_year__name__icontains=search)
                | Q(subject__name__icontains=search)
                | Q(subject__code__icontains=search)
                | Q(subject__category__name__icontains=search)
            )

        return queryset

    def create(self, request, *args, **kwargs):
        """
        Create a new class subject assignment.
        """
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            try:
                class_subject = serializer.save()
                response_serializer = ClassSubjectSerializer(class_subject)
                return Response(
                    response_serializer.data, status=status.HTTP_201_CREATED
                )
            except Exception as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, *args, **kwargs):
        """
        Update a class subject assignment.
        """
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=False)
        if serializer.is_valid():
            try:
                class_subject = serializer.save()
                response_serializer = ClassSubjectSerializer(class_subject)
                return Response(response_serializer.data)
            except Exception as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def partial_update(self, request, *args, **kwargs):
        """
        Partial update a class subject assignment.
        """
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        if serializer.is_valid():
            try:
                class_subject = serializer.save()
                response_serializer = ClassSubjectSerializer(class_subject)
                return Response(response_serializer.data)
            except Exception as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["post"])
    def bulk_assign(self, request):
        """
        Bulk assign multiple subjects to a class.
        """
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            try:
                result = serializer.save()
                return Response(result, status=status.HTTP_201_CREATED)
            except Exception as e:
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for class subjects.
        """
        try:
            total_assignments = self.get_queryset().count()

            # Get counts by academic year
            by_year = {}
            for assignment in self.get_queryset():
                year = assignment.academic_class.academic_year.name
                if year not in by_year:
                    by_year[year] = 0
                by_year[year] += 1

            # Get counts by standard
            by_standard = {}
            for assignment in self.get_queryset():
                standard = assignment.academic_class.standard.name
                if standard not in by_standard:
                    by_standard[standard] = 0
                by_standard[standard] += 1

            # Get counts by subject type
            by_subject_type = {
                "mandatory": self.get_queryset()
                .filter(subject__subject_type="mandatory")
                .count(),
                "elective": self.get_queryset()
                .filter(subject__subject_type="elective")
                .count(),
                "language": self.get_queryset()
                .filter(subject__subject_type="language")
                .count(),
            }

            stats = {
                "total": total_assignments,
                "by_year": by_year,
                "by_standard": by_standard,
                "by_subject_type": by_subject_type,
            }

            return Response(stats)
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def by_class(self, request):
        """
        Get all subjects assigned to a specific class.
        """
        class_id = request.query_params.get("class_id")
        if not class_id:
            return Response(
                {"error": "class_id parameter is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            class_subjects = ClassSubject.objects.filter(
                academic_class_id=class_id
            ).select_related("subject", "subject__category")
            serializer = self.get_serializer(class_subjects, many=True)
            return Response(serializer.data)
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def available_subjects(self, request):
        """
        Get subjects available for assignment to a specific class.
        """
        class_id = request.query_params.get("class_id")
        if not class_id:
            return Response(
                {"error": "class_id parameter is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get the academic class
            academic_class = AcademicClass.objects.get(id=class_id)

            # Get all active subjects for this standard
            all_subjects = Subject.objects.filter(
                standard=academic_class.standard, is_active=True
            ).select_related("category", "subject_group")

            # Get already assigned subjects
            assigned_subject_ids = ClassSubject.objects.filter(
                academic_class=academic_class
            ).values_list("subject_id", flat=True)

            # Filter out already assigned subjects
            available_subjects = all_subjects.exclude(id__in=assigned_subject_ids)

            # Format response
            subject_data = []
            for subject in available_subjects:
                subject_data.append(
                    {
                        "id": subject.id,
                        "name": subject.name,
                        "code": subject.code,
                        "subject_type": subject.subject_type,
                        "category": (
                            {
                                "id": subject.category.id,
                                "name": subject.category.name,
                                "code": subject.category.code,
                            }
                            if subject.category
                            else None
                        ),
                        "subject_group": (
                            {
                                "id": subject.subject_group.id,
                                "name": subject.subject_group.name,
                                "code": subject.subject_group.code,
                            }
                            if subject.subject_group
                            else None
                        ),
                    }
                )

            return Response(subject_data)
        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found."}, status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def check_availability(self, request):
        """
        Check if a subject can be assigned to a class.
        """
        class_id = request.query_params.get("class_id")
        subject_id = request.query_params.get("subject_id")

        if not class_id or not subject_id:
            return Response(
                {"error": "Both class_id and subject_id parameters are required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Check if assignment already exists
            exists = ClassSubject.objects.filter(
                academic_class_id=class_id, subject_id=subject_id
            ).exists()

            # Check if academic class exists and is active
            academic_class = AcademicClass.objects.filter(
                id=class_id, is_active=True
            ).first()

            # Check if subject exists and is active
            subject = Subject.objects.filter(id=subject_id, is_active=True).first()

            # Check if subject belongs to the same standard
            can_assign = False
            if academic_class and subject:
                can_assign = subject.standard == academic_class.standard

            return Response(
                {
                    "already_assigned": exists,
                    "can_assign": can_assign,
                    "academic_class_exists": academic_class is not None,
                    "subject_exists": subject is not None,
                    "standard_match": can_assign,
                }
            )
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"], url_path="books-by-class")
    def books_by_class(self, request):
        """
        GET /academics/class-subjects/books-by-class/?class_id=<id>

        Returns all subjects assigned to the given class together with their
        book document info. Subjects without a book are included so the caller
        knows the full subject list and which ones have books.
        """
        class_id = request.query_params.get("class_id")
        if not class_id:
            return Response(
                {"error": "class_id parameter is required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            academic_class = AcademicClass.objects.select_related(
                "standard", "section", "academic_year"
            ).get(id=class_id)
        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        class_subjects = ClassSubject.objects.filter(
            academic_class=academic_class
        ).select_related(
            "subject",
            "subject__category",
            "subject__subject_group",
            "subject__standard",
        )

        subjects_data = []
        for cs in class_subjects:
            subj = cs.subject
            book_url = None
            book_name = None
            if subj.book_document:
                book_url = request.build_absolute_uri(subj.book_document.url)
                book_name = os.path.basename(subj.book_document.name)

            subjects_data.append(
                {
                    "class_subject_id": cs.id,
                    "subject_id": subj.id,
                    "subject_name": subj.name,
                    "subject_code": subj.code,
                    "subject_type": subj.subject_type,
                    "is_active": subj.is_active,
                    "category": (
                        {
                            "id": subj.category.id,
                            "name": subj.category.name,
                            "code": subj.category.code,
                        }
                        if subj.category
                        else None
                    ),
                    "subject_group": (
                        {
                            "id": subj.subject_group.id,
                            "name": subj.subject_group.name,
                        }
                        if subj.subject_group
                        else None
                    ),
                    "has_book": bool(subj.book_document),
                    "book_document_url": book_url,
                    "book_document_name": book_name,
                }
            )

        return Response(
            {
                "class_id": academic_class.id,
                "class_name": f"{academic_class.standard.name} - {academic_class.section.name if academic_class.section else 'All'}",
                "standard": {
                    "id": academic_class.standard.id,
                    "name": academic_class.standard.name,
                    "code": academic_class.standard.code,
                },
                "academic_year": (
                    academic_class.academic_year.name
                    if academic_class.academic_year
                    else None
                ),
                "total_subjects": len(subjects_data),
                "subjects_with_books": sum(
                    1 for s in subjects_data if s["has_book"]
                ),
                "subjects": subjects_data,
            }
        )


class StudentEnrollmentViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing student enrollments.
    """

    serializer_class = StudentEnrollmentSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    ordering_fields = ["roll_number", "admission_date", "created_at"]
    ordering = ["roll_number"]

    search_fields = [
        "student__first_name",
        "student__last_name",
        "student__student_id",
        "student__admission_number",
        "student__roll_number",
        "roll_number",
        "student__student_parents__parent__first_name",
        "student__student_parents__parent__last_name",
        "academic_class__standard__name",
        "academic_class__section__name",
    ]

    def get_queryset(self):
        queryset = StudentEnrollment.objects.select_related(
            "student",
            "academic_class",
            "academic_class__standard",
            "academic_class__section",
            "academic_class__academic_year",  # Add this to select related
        ).distinct()

        # Apply filters from query parameters
        student_id = self.request.query_params.get("student")
        if student_id:
            queryset = queryset.filter(student_id=student_id)

        academic_class_id = self.request.query_params.get("academic_class")
        if academic_class_id:
            queryset = queryset.filter(academic_class_id=academic_class_id)

        # NEW: Filter by academic year through the academic_class relationship
        academic_year_id = self.request.query_params.get("academic_year")
        if academic_year_id:
            queryset = queryset.filter(
                academic_class__academic_year_id=academic_year_id
            )

        is_active = self.request.query_params.get("is_active")
        if is_active is not None:
            queryset = queryset.filter(is_active=is_active.lower() == "true")

        admission_date_gte = self.request.query_params.get("admission_date__gte")
        if admission_date_gte:
            queryset = queryset.filter(admission_date__gte=admission_date_gte)

        admission_date_lte = self.request.query_params.get("admission_date__lte")
        if admission_date_lte:
            queryset = queryset.filter(admission_date__lte=admission_date_lte)

        created_at_gte = self.request.query_params.get("created_at__gte")
        if created_at_gte:
            queryset = queryset.filter(created_at__gte=created_at_gte)

        created_at_lte = self.request.query_params.get("created_at__lte")
        if created_at_lte:
            queryset = queryset.filter(created_at__lte=created_at_lte)

        return queryset

    def perform_create(self, serializer):
        serializer.save()

    @action(detail=False, methods=["post"])
    def bulk_create(self, request):
        """
        Bulk create enrollments for multiple students
        """
        serializer = StudentEnrollmentCreateSerializer(data=request.data)
        if serializer.is_valid():
            student_ids = serializer.validated_data["student_ids"]
            academic_class_id = serializer.validated_data["academic_class_id"]
            admission_date = serializer.validated_data.get(
                "admission_date", date.today()
            )

            try:
                academic_class = AcademicClass.objects.get(id=academic_class_id)

                enrollments = []
                errors = []

                with transaction.atomic():
                    for i, student_id in enumerate(student_ids):
                        try:
                            student = Student.objects.get(id=student_id)

                            # Check if student is already enrolled
                            existing = StudentEnrollment.objects.filter(
                                student=student, academic_class=academic_class
                            ).first()

                            if existing:
                                errors.append(
                                    {
                                        "student_id": student_id,
                                        "error": f"Already enrolled (Enrollment ID: {existing.id})",
                                    }
                                )
                                continue

                            # Get next available roll number
                            last_roll = (
                                StudentEnrollment.objects.filter(
                                    academic_class=academic_class
                                )
                                .order_by("-roll_number")
                                .first()
                            )

                            roll_number = (
                                (last_roll.roll_number + 1) if last_roll else (i + 1)
                            )

                            enrollment = StudentEnrollment.objects.create(
                                student=student,
                                academic_class=academic_class,
                                roll_number=roll_number,
                                admission_date=admission_date,
                                is_active=True,
                            )

                            enrollments.append(enrollment)

                        except Student.DoesNotExist:
                            errors.append(
                                {"student_id": student_id, "error": "Student not found"}
                            )
                        except Exception as e:
                            errors.append({"student_id": student_id, "error": str(e)})

                if errors and not enrollments:
                    return Response(
                        {"errors": errors}, status=status.HTTP_400_BAD_REQUEST
                    )

                result_serializer = StudentEnrollmentSerializer(enrollments, many=True)

                response_data = {
                    "success_count": len(enrollments),
                    "error_count": len(errors),
                    "enrollments": result_serializer.data,
                }

                if errors:
                    response_data["errors"] = errors
                    return Response(response_data, status=status.HTTP_207_MULTI_STATUS)

                return Response(response_data, status=status.HTTP_201_CREATED)

            except AcademicClass.DoesNotExist:
                return Response(
                    {"error": "Academic class not found"},
                    status=status.HTTP_404_NOT_FOUND,
                )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=False, methods=["get"], url_path="available-students")
    def available_students(self, request):
        academic_year_id = request.query_params.get("academic_year_id")
        if not academic_year_id:
            return Response(
                {"error": "academic_year_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )
 
        # ── Validate academic year ────────────────────────────────────────────
        try:
            academic_year = AcademicYear.objects.get(id=academic_year_id)
        except AcademicYear.DoesNotExist:
            return Response(
                {"error": "Academic year not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
 
        academic_class_id = request.query_params.get("academic_class_id")
        search = request.query_params.get("search", "").strip()
 
        # ── Student IDs already enrolled in this academic year ────────────────
        # Any enrollment whose academic_class belongs to the requested year.
        enrolled_student_ids = StudentEnrollment.objects.filter(
            academic_class__academic_year_id=academic_year_id,
            is_active=True,
        ).values_list("student_id", flat=True)
 
        # ── Also exclude students enrolled in the specific target class ───────
        # (catches inactive-then-reactivated edge cases)
        if academic_class_id:
            enrolled_in_class_ids = StudentEnrollment.objects.filter(
                academic_class_id=academic_class_id,
            ).values_list("student_id", flat=True)
            # Union both sets
            excluded_ids = set(enrolled_student_ids) | set(enrolled_in_class_ids)
        else:
            excluded_ids = set(enrolled_student_ids)
 
        # ── Base queryset: only active students, exclude already-enrolled ─────
        students_qs = Student.objects.filter(is_active=True).exclude(
            id__in=excluded_ids
        )
 
        # ── Search filter ─────────────────────────────────────────────────────
        if search:
            students_qs = students_qs.filter(
                Q(first_name__icontains=search)
                | Q(last_name__icontains=search)
                | Q(admission_number__icontains=search)
                | Q(student_id__icontains=search)
            )
 
        students_qs = students_qs.order_by("first_name", "last_name")[:100]  # cap
 
        # ── Serialise ─────────────────────────────────────────────────────────
        student_data = []
        for student in students_qs:
            full_name = (
                student.full_name
                or f"{student.first_name or ''} {student.last_name or ''}".strip()
                or "Unknown"
            )
            student_data.append(
                {
                    "id": student.id,
                    "first_name": student.first_name or "",
                    "last_name": student.last_name or "",
                    "full_name": full_name,
                    "student_id": student.student_id or "",
                    "admission_number": student.admission_number or "",
                    "gender": student.gender or "",
                    "profile_image": (
                        request.build_absolute_uri(student.profile_image.url)
                        if student.profile_image
                        else None
                    ),
                }
            )
 
        return Response(
            {
                "academic_year": {
                    "id": academic_year.id,
                    "name": academic_year.name,
                },
                "total_available": len(student_data),
                "students": student_data,
            },
            status=status.HTTP_200_OK,
        )
 
    # ─────────────────────────────────────────────────────────────────────────────
# REPLACE the existing available_students action inside StudentEnrollmentViewSet
# in academics/views.py
#
# URL:  GET /academics/student-enrollments/available-students/
#       ?academic_year_id=<id>          ← required
#       &academic_class_id=<id>         ← optional
#       &search=<query>                 ← optional  (server-side)
#       &page=<int>                     ← optional, default 1
#       &page_size=<int>                ← optional, default 30, max 100
# ─────────────────────────────────────────────────────────────────────────────

    @action(detail=False, methods=["get"], url_path="available-students")
    def available_students(self, request):
        """
        Return PAGINATED students who are NOT enrolled in ANY active enrollment
        belonging to the given academic year.

        If academic_class_id is supplied, students already enrolled in THAT
        specific class (active OR inactive) are also excluded to prevent
        re-enrolment edge cases.

        Search is done SERVER-SIDE against first_name, last_name,
        admission_number, and student_id.
        """

        # ── Validate required param ───────────────────────────────────────────
        academic_year_id = request.query_params.get("academic_year_id")
        if not academic_year_id:
            return Response(
                {"error": "academic_year_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            academic_year = AcademicYear.objects.get(id=academic_year_id)
        except AcademicYear.DoesNotExist:
            return Response(
                {"error": "Academic year not found"},
                status=status.HTTP_404_NOT_FOUND,
            )

        academic_class_id = request.query_params.get("academic_class_id")
        search = request.query_params.get("search", "").strip()

        # ── Pagination params ─────────────────────────────────────────────────
        try:
            page = max(1, int(request.query_params.get("page", 1)))
            page_size = min(100, max(1, int(request.query_params.get("page_size", 30))))
        except (ValueError, TypeError):
            page, page_size = 1, 30

        # ── Collect excluded student IDs ──────────────────────────────────────
        # Everyone enrolled (active) in ANY class of this academic year
        enrolled_in_year = set(
            StudentEnrollment.objects.filter(
                academic_class__academic_year_id=academic_year_id,
                is_active=True,
            ).values_list("student_id", flat=True)
        )

        # Everyone ever enrolled in the specific target class (active or inactive)
        if academic_class_id:
            enrolled_in_class = set(
                StudentEnrollment.objects.filter(
                    academic_class_id=academic_class_id,
                ).values_list("student_id", flat=True)
            )
        else:
            enrolled_in_class = set()

        excluded_ids = enrolled_in_year | enrolled_in_class

        # ── Base queryset ─────────────────────────────────────────────────────
        qs = Student.objects.filter(is_active=True).exclude(id__in=excluded_ids)

        # ── Server-side search ────────────────────────────────────────────────
        if search:
            qs = qs.filter(
                Q(first_name__icontains=search)
                | Q(last_name__icontains=search)
                | Q(admission_number__icontains=search)
                | Q(student_id__icontains=search)
            )

        qs = qs.order_by("first_name", "last_name")

        # ── Paginate ──────────────────────────────────────────────────────────
        total_count = qs.count()
        offset = (page - 1) * page_size
        page_qs = qs[offset: offset + page_size]

        # ── Serialise ─────────────────────────────────────────────────────────
        student_data = []
        for student in page_qs:
            full_name = (
                student.full_name
                or f"{student.first_name or ''} {student.last_name or ''}".strip()
                or "Unknown"
            )
            student_data.append(
                {
                    "id": student.id,
                    "first_name": student.first_name or "",
                    "last_name": student.last_name or "",
                    "full_name": full_name,
                    "student_id": student.student_id or "",
                    "admission_number": student.admission_number or "",
                    "gender": student.gender or "",
                    "profile_image": (
                        request.build_absolute_uri(student.profile_image.url)
                        if student.profile_image
                        else None
                    ),
                }
            )

        return Response(
            {
                "academic_year": {
                    "id": academic_year.id,
                    "name": academic_year.name,
                },
                "count": total_count,
                "page": page,
                "page_size": page_size,
                "total_pages": (total_count + page_size - 1) // page_size if page_size else 1,
                "has_next": (offset + page_size) < total_count,
                "has_previous": page > 1,
                "total_available": total_count,
                "students": student_data,
            },
            status=status.HTTP_200_OK,
        )
    
    
    @action(detail=False, methods=["post"], url_path="transfer-to-next-year")
    def transfer_to_next_year(self, request):
        """
        Transfer class and students to next academic year.
        """
        logger.info(f"Transfer to next year request received: {request.data}")
        logger.info(f"User: {request.user}, IP: {request.META.get('REMOTE_ADDR')}")

        serializer = TransferToNextYearSerializer(data=request.data)
        if serializer.is_valid():
            try:
                source_class = serializer.validated_data["source_class"]
                target_academic_year = serializer.validated_data["target_academic_year"]
                target_class = serializer.validated_data.get("target_class")
                copy_class_details = serializer.validated_data.get(
                    "copy_class_details", True
                )
                copy_students = serializer.validated_data.get("copy_students", True)
                keep_original_active = serializer.validated_data.get(
                    "keep_original_active", False
                )
                transfer_date = serializer.validated_data.get(
                    "transfer_date", date.today()
                )

                logger.info(
                    f"Source class: {source_class.id} - {source_class.standard.name if source_class.standard else ''} {source_class.section.name if source_class.section else ''}"
                )
                logger.info(
                    f"Target academic year: {target_academic_year.id} - {target_academic_year.name}"
                )
                logger.info(f"Copy class details: {copy_class_details}")
                logger.info(f"Copy students: {copy_students}")
                logger.info(f"Keep original active: {keep_original_active}")

                # Generate class name from standard and section
                source_class_name = f"{source_class.standard.name if source_class.standard else 'Unknown'} - {source_class.section.name if source_class.section else 'All'}"

                # Check if target class exists
                if not target_class and copy_class_details:
                    # Check if class already exists in target year with same standard and section
                    existing_class = AcademicClass.objects.filter(
                        academic_year=target_academic_year,
                        standard=source_class.standard,
                        section=source_class.section,
                    ).first()

                    if existing_class:
                        target_class = existing_class
                        logger.info(f"Using existing target class: {target_class.id}")
                    else:
                        # Create new class in target year
                        logger.info("Creating new target class")
                        target_class = AcademicClass.objects.create(
                            academic_year=target_academic_year,
                            standard=source_class.standard,
                            section=source_class.section,
                            class_teacher=(
                                source_class.class_teacher
                                if copy_class_details
                                else None
                            ),
                            assistant_teacher=(
                                source_class.assistant_teacher
                                if copy_class_details
                                else None
                            ),
                            room_number=(
                                source_class.room_number if copy_class_details else ""
                            ),
                            timetable_document=(
                                source_class.timetable_document
                                if copy_class_details
                                else None
                            ),
                            max_strength=(
                                source_class.max_strength
                                if copy_class_details
                                else source_class.max_strength
                            ),
                            annual_fee=(
                                source_class.annual_fee
                                if copy_class_details
                                else source_class.annual_fee
                            ),
                            passing_percentage=(
                                source_class.passing_percentage
                                if copy_class_details
                                else source_class.passing_percentage
                            ),
                            min_attendance_percentage=(
                                source_class.min_attendance_percentage
                                if copy_class_details
                                else source_class.min_attendance_percentage
                            ),
                            notes=f"Transferred from {source_class.academic_year.name} - {source_class_name} on {transfer_date}",
                            is_active=True,
                            is_frozen=False,
                        )
                        logger.info(
                            f"New target class created with ID: {target_class.id}"
                        )
                elif target_class:
                    logger.info(f"Using provided target class: {target_class.id}")

                if not target_class:
                    logger.error("Target class not found and cannot be created")
                    return Response(
                        {"error": "Target class not found and cannot be created"},
                        status=status.HTTP_400_BAD_REQUEST,
                    )

                # Transfer students if requested
                students_transferred = 0
                enrollment_errors = []

                if copy_students:
                    # Get all active enrollments in source class
                    source_enrollments = StudentEnrollment.objects.filter(
                        academic_class=source_class, is_active=True
                    ).select_related("student")

                    logger.info(
                        f"Found {source_enrollments.count()} active enrollments in source class"
                    )

                    if source_enrollments.count() == 0:
                        logger.warning("No active enrollments found in source class")
                    else:
                        # Get next roll number in target class
                        last_enrollment = (
                            StudentEnrollment.objects.filter(
                                academic_class=target_class
                            )
                            .order_by("-roll_number")
                            .first()
                        )

                        if last_enrollment:
                            next_roll = last_enrollment.roll_number + 1
                            logger.info(
                                f"Last roll number in target class: {last_enrollment.roll_number}, starting from: {next_roll}"
                            )
                        else:
                            next_roll = 1
                            logger.info(
                                "No existing enrollments in target class, starting roll number from 1"
                            )

                        # Create new enrollments in target class
                        current_roll = next_roll
                        for enrollment in source_enrollments:
                            try:
                                # Check if student already enrolled in target class
                                existing_enrollment = StudentEnrollment.objects.filter(
                                    student=enrollment.student,
                                    academic_class=target_class,
                                    is_active=True,
                                ).first()

                                if existing_enrollment:
                                    logger.warning(
                                        f"Student {enrollment.student.id} already enrolled in target class (Enrollment ID: {existing_enrollment.id})"
                                    )
                                    enrollment_errors.append(
                                        {
                                            "student_id": enrollment.student.id,
                                            "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                                            "error": f"Already enrolled (Enrollment ID: {existing_enrollment.id})",
                                        }
                                    )
                                    continue

                                logger.info(
                                    f"Creating enrollment for student {enrollment.student.id} with roll number {current_roll}"
                                )

                                # Create new enrollment
                                new_enrollment = StudentEnrollment.objects.create(
                                    student=enrollment.student,
                                    academic_class=target_class,
                                    roll_number=current_roll,
                                    admission_date=transfer_date,
                                    is_active=True,
                                )

                                logger.info(
                                    f"Created new enrollment ID: {new_enrollment.id} for student {enrollment.student.id}"
                                )

                                # Deactivate old enrollment if not keeping original active
                                if not keep_original_active:
                                    enrollment.is_active = False
                                    enrollment.save()
                                    logger.info(
                                        f"Deactivated old enrollment ID: {enrollment.id}"
                                    )

                                students_transferred += 1
                                current_roll += 1

                            except Exception as e:
                                logger.error(
                                    f"Error creating enrollment for student {enrollment.student.id}: {str(e)}"
                                )
                                enrollment_errors.append(
                                    {
                                        "student_id": enrollment.student.id,
                                        "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                                        "error": str(e),
                                    }
                                )
                else:
                    logger.info("Skipping student transfer as copy_students is False")

                # Update source class status if not keeping original active
                if not keep_original_active:
                    source_class.is_active = False
                    source_class.save()
                    logger.info(f"Deactivated source class ID: {source_class.id}")

                # Serialize the result using proper serializer
                result = {
                    "message": f"Successfully transferred {students_transferred} students",
                    "source_class": {
                        "id": source_class.id,
                        "name": source_class_name,
                        "academic_year": (
                            source_class.academic_year.name
                            if source_class.academic_year
                            else "N/A"
                        ),
                        "standard": (
                            source_class.standard.name
                            if source_class.standard
                            else "N/A"
                        ),
                        "section": (
                            source_class.section.name if source_class.section else "N/A"
                        ),
                    },
                    "target_class": {
                        "id": target_class.id,
                        "name": f"{target_class.standard.name if target_class.standard else 'Unknown'} - {target_class.section.name if target_class.section else 'All'}",
                        "academic_year": (
                            target_class.academic_year.name
                            if target_class.academic_year
                            else "N/A"
                        ),
                        "standard": (
                            target_class.standard.name
                            if target_class.standard
                            else "N/A"
                        ),
                        "section": (
                            target_class.section.name if target_class.section else "N/A"
                        ),
                    },
                    "students_transferred": students_transferred,
                    "transfer_date": transfer_date,
                    "total_source_enrollments": (
                        source_enrollments.count() if copy_students else 0
                    ),
                }

                if enrollment_errors:
                    result["enrollment_errors"] = enrollment_errors
                    result["errors_count"] = len(enrollment_errors)
                    logger.warning(f"Completed with {len(enrollment_errors)} errors")

                logger.info(
                    f"Transfer completed successfully. Students transferred: {students_transferred}"
                )
                return Response(result, status=status.HTTP_200_OK)

            except AcademicClass.DoesNotExist as e:
                logger.error(f"Academic class not found: {str(e)}")
                return Response(
                    {"error": "Academic class not found"},
                    status=status.HTTP_404_NOT_FOUND,
                )
            except AcademicYear.DoesNotExist as e:
                logger.error(f"Academic year not found: {str(e)}")
                return Response(
                    {"error": "Academic year not found"},
                    status=status.HTTP_404_NOT_FOUND,
                )
            except Exception as e:
                logger.error(
                    f"Unexpected error in transfer_to_next_year: {str(e)}",
                    exc_info=True,
                )
                return Response(
                    {"error": f"Transfer failed: {str(e)}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        logger.error(f"Serializer validation errors: {serializer.errors}")
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get enrollment statistics
        """
        queryset = self.get_queryset()

        total_enrollments = queryset.count()
        active_enrollments = queryset.filter(is_active=True).count()
        inactive_enrollments = total_enrollments - active_enrollments

        # Enrollment by class
        enrollments_by_class = (
            queryset.values(
                "academic_class_id",
                "academic_class__standard__name",
                "academic_class__section__name",
            )
            .annotate(
                count=Count("id"), active_count=Count("id", filter=Q(is_active=True))
            )
            .order_by(
                "academic_class__standard__name",
                "academic_class__section__name",
            )
        )

        # Recent enrollments
        recent_enrollments = queryset.order_by("-created_at")[:10]
        recent_serializer = self.get_serializer(recent_enrollments, many=True)

        return Response(
            {
                "total_enrollments": total_enrollments,
                "active_enrollments": active_enrollments,
                "inactive_enrollments": inactive_enrollments,
                "enrollments_by_class": list(enrollments_by_class),
                "recent_enrollments": recent_serializer.data,
            }
        )

    @action(detail=True, methods=["post"])
    def transfer(self, request, pk=None):
        """
        Transfer student to another class
        """
        enrollment = self.get_object()
        new_class_id = request.data.get("new_class_id")

        if not new_class_id:
            return Response(
                {"error": "new_class_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            new_class = AcademicClass.objects.get(id=new_class_id)

            # Check if student is already enrolled in new class
            existing = StudentEnrollment.objects.filter(
                student=enrollment.student, academic_class=new_class
            ).first()

            if existing:
                return Response(
                    {"error": f"Student is already enrolled in {new_class.name}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Deactivate old enrollment
            enrollment.is_active = False
            enrollment.save()

            # Create new enrollment
            last_roll = (
                StudentEnrollment.objects.filter(academic_class=new_class)
                .order_by("-roll_number")
                .first()
            )

            new_roll_number = (last_roll.roll_number + 1) if last_roll else 1

            new_enrollment = StudentEnrollment.objects.create(
                student=enrollment.student,
                academic_class=new_class,
                roll_number=new_roll_number,
                admission_date=date.today(),
                is_active=True,
            )

            new_serializer = self.get_serializer(new_enrollment)

            return Response(
                {
                    "message": "Student transferred successfully",
                    "old_enrollment": self.get_serializer(enrollment).data,
                    "new_enrollment": new_serializer.data,
                }
            )

        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found"}, status=status.HTTP_404_NOT_FOUND
            )


class StudentSubjectGroupViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing student subject group assignments.
    Only applicable for higher secondary students (11th & 12th standards).
    """

    queryset = StudentSubjectGroup.objects.all()
    serializer_class = StudentSubjectGroupSerializer
    permission_classes = [permissions.IsAuthenticated]

    # Define filter fields
    filterset_fields = {
        "is_active": ["exact"],
        "enrollment__academic_class": ["exact"],
        "enrollment__student": ["exact"],
        "subject_group": ["exact"],
    }

    # Define search fields
    search_fields = [
        "enrollment__student__first_name",
        "enrollment__student__last_name",
        "enrollment__student__admission_number",
        "subject_group__name",
        "subject_group__code",
    ]

    # Define ordering fields
    ordering_fields = [
        "created_at",
        "enrollment__student__first_name",
        "subject_group__name",
    ]
    ordering = ["-created_at"]

    def get_queryset(self):
        """
        Get queryset with proper relationship paths.
        """
        queryset = StudentSubjectGroup.objects.select_related(
            "enrollment",
            "enrollment__student",
            "enrollment__academic_class",
            "enrollment__academic_class__standard",
            "enrollment__academic_class__section",
            "enrollment__academic_class__academic_year",
            "subject_group",
            "subject_group__standard",
        )

        # Apply filters
        queryset = self.apply_filters(queryset)

        return queryset

    def apply_filters(self, queryset):
        """
        Apply custom filters from query parameters.
        """
        params = self.request.query_params

        # Filter by academic class
        academic_class_id = params.get("academic_class")
        if academic_class_id and academic_class_id != "all":
            queryset = queryset.filter(enrollment__academic_class_id=academic_class_id)

        # Filter by subject group
        subject_group_id = params.get("subject_group")
        if subject_group_id and subject_group_id != "all":
            queryset = queryset.filter(subject_group_id=subject_group_id)

        # Filter by status
        status_param = params.get("status")
        if status_param and status_param != "all":
            if status_param == "active":
                queryset = queryset.filter(is_active=True)
            elif status_param == "inactive":
                queryset = queryset.filter(is_active=False)

        # Filter by academic year
        academic_year_id = params.get("academic_year")
        if academic_year_id:
            queryset = queryset.filter(
                enrollment__academic_class__academic_year_id=academic_year_id
            )

        # Filter by standard
        standard_id = params.get("standard")
        if standard_id:
            queryset = queryset.filter(
                enrollment__academic_class__standard_id=standard_id
            )

        # Filter by section
        section_id = params.get("section")
        if section_id:
            queryset = queryset.filter(
                enrollment__academic_class__section_id=section_id
            )

        # Filter by student
        student_id = params.get("student")
        if student_id:
            queryset = queryset.filter(enrollment__student_id=student_id)

        # Filter by date range
        created_at_gte = params.get("created_at__gte")
        if created_at_gte:
            queryset = queryset.filter(created_at__gte=created_at_gte)

        created_at_lte = params.get("created_at__lte")
        if created_at_lte:
            queryset = queryset.filter(created_at__lte=created_at_lte)

        # Search query
        search_query = params.get("search")
        if search_query:
            queryset = queryset.filter(
                Q(enrollment__student__first_name__icontains=search_query)
                | Q(enrollment__student__last_name__icontains=search_query)
                | Q(enrollment__student__admission_number__icontains=search_query)
                | Q(subject_group__name__icontains=search_query)
                | Q(subject_group__code__icontains=search_query)
                | Q(enrollment__academic_class__standard__name__icontains=search_query)
                | Q(enrollment__academic_class__section__code__icontains=search_query)
            )

        return queryset

    def list(self, request, *args, **kwargs):
        """
        Override list method to handle pagination.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Pagination
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["post"])
    def bulk_create(self, request):
        """
        Bulk create student subject group assignments.
        """
        serializer = StudentSubjectGroupBulkCreateSerializer(data=request.data)
        if serializer.is_valid():
            enrollment_ids = serializer.validated_data["enrollment_ids"]
            subject_group_id = serializer.validated_data["subject_group_id"]
            is_active = serializer.validated_data.get("is_active", True)

            created_groups = []
            errors = []

            try:
                with transaction.atomic():
                    for enrollment_id in enrollment_ids:
                        try:
                            enrollment = StudentEnrollment.objects.get(id=enrollment_id)

                            # Check if assignment already exists
                            if StudentSubjectGroup.objects.filter(
                                enrollment=enrollment, subject_group_id=subject_group_id
                            ).exists():
                                errors.append(
                                    {
                                        "enrollment_id": enrollment_id,
                                        "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                                        "error": "Student is already assigned to this subject group",
                                    }
                                )
                                continue

                            # Create student subject group
                            student_subject_group = StudentSubjectGroup.objects.create(
                                enrollment=enrollment,
                                subject_group_id=subject_group_id,
                                is_active=is_active,
                            )
                            created_groups.append(student_subject_group)

                        except StudentEnrollment.DoesNotExist:
                            errors.append(
                                {
                                    "enrollment_id": enrollment_id,
                                    "error": "Enrollment not found",
                                }
                            )
                        except Exception as e:
                            errors.append(
                                {"enrollment_id": enrollment_id, "error": str(e)}
                            )

                # Prepare response
                if errors and not created_groups:
                    return Response(
                        {"errors": errors}, status=status.HTTP_400_BAD_REQUEST
                    )

                result_serializer = StudentSubjectGroupSerializer(
                    created_groups, many=True
                )

                response_data = {
                    "message": f"Successfully assigned {len(created_groups)} students to subject group",
                    "success_count": len(created_groups),
                    "error_count": len(errors),
                    "student_subject_groups": result_serializer.data,
                }

                if errors:
                    response_data["errors"] = errors
                    return Response(response_data, status=status.HTTP_207_MULTI_STATUS)

                return Response(response_data, status=status.HTTP_201_CREATED)

            except Exception as e:
                logger.error(f"Bulk create transaction failed: {str(e)}")
                return Response(
                    {"error": f"Bulk create failed: {str(e)}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["post"])
    def bulk_update_status(self, request):
        """
        Bulk update student subject group status.
        """
        serializer = StudentSubjectGroupBulkUpdateSerializer(data=request.data)
        if serializer.is_valid():
            group_ids = serializer.validated_data["student_subject_group_ids"]
            is_active = serializer.validated_data["is_active"]

            try:
                with transaction.atomic():
                    updated_count = StudentSubjectGroup.objects.filter(
                        id__in=group_ids
                    ).update(is_active=is_active)

                return Response(
                    {
                        "message": f"Successfully updated {updated_count} student subject group(s)",
                        "updated_count": updated_count,
                        "is_active": is_active,
                    }
                )

            except Exception as e:
                logger.error(f"Bulk update failed: {str(e)}")
                return Response(
                    {"error": f"Bulk update failed: {str(e)}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get student subject group statistics.
        """
        queryset = self.filter_queryset(self.get_queryset())

        try:
            # Basic statistics
            total_assignments = queryset.count()
            active_assignments = queryset.filter(is_active=True).count()
            inactive_assignments = total_assignments - active_assignments

            # Group by subject group
            assignments_by_subject_group = (
                queryset.values(
                    "subject_group__id", "subject_group__name", "subject_group__code"
                )
                .annotate(
                    total=Count("id"),
                    active=Count("id", filter=Q(is_active=True)),
                    inactive=Count("id", filter=Q(is_active=False)),
                )
                .order_by("subject_group__name")
            )

            # Group by academic class
            assignments_by_class = (
                queryset.values(
                    "enrollment__academic_class__id",
                    "enrollment__academic_class__standard__name",
                    "enrollment__academic_class__section__code",
                )
                .annotate(
                    total=Count("id"),
                    active=Count("id", filter=Q(is_active=True)),
                    inactive=Count("id", filter=Q(is_active=False)),
                )
                .order_by("enrollment__academic_class__standard__name")
            )

            return Response(
                {
                    "total_assignments": total_assignments,
                    "active_assignments": active_assignments,
                    "inactive_assignments": inactive_assignments,
                    "assignments_by_subject_group": list(assignments_by_subject_group),
                    "assignments_by_class": list(assignments_by_class),
                }
            )

        except Exception as e:
            logger.error(f"Error generating statistics: {str(e)}")
            return Response(
                {"error": f"Error generating statistics: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"], url_path="available-students")
    def available_students(self, request):
        """
        Get students available for assignment to a subject group.
        GET /api/student-subject-groups/available_students/?subject_group_id=1&academic_class_id=1
        """
        subject_group_id = request.query_params.get("subject_group_id")
        academic_class_id = request.query_params.get("academic_class_id")

        if not subject_group_id or not academic_class_id:
            return Response(
                {"error": "subject_group_id and academic_class_id are required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get subject group
            subject_group = SubjectGroup.objects.get(id=subject_group_id)

            # Get all active enrollments in the class
            class_enrollments = StudentEnrollment.objects.filter(
                academic_class_id=academic_class_id, is_active=True
            ).select_related("student")

            # Get already assigned enrollment IDs
            assigned_enrollment_ids = StudentSubjectGroup.objects.filter(
                subject_group_id=subject_group_id, is_active=True
            ).values_list("enrollment_id", flat=True)

            # Filter out already assigned students
            available_enrollments = []
            for enrollment in class_enrollments:
                if enrollment.id not in assigned_enrollment_ids:
                    available_enrollments.append(
                        {
                            "enrollment_id": enrollment.id,
                            "student_id": enrollment.student.id,
                            "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                            "admission_number": enrollment.student.admission_number,
                            "roll_number": enrollment.roll_number,
                        }
                    )

            # Get class details safely
            class_details = {}
            try:
                academic_class = AcademicClass.objects.get(id=academic_class_id)
                class_details = {
                    "id": academic_class_id,
                    "class_name": f"{academic_class.standard.name if academic_class.standard else 'Unknown'} - {academic_class.section.code if academic_class.section else 'Unknown'}",
                }
            except AcademicClass.DoesNotExist:
                class_details = {"id": academic_class_id, "class_name": "Unknown Class"}

            return Response(
                {
                    "subject_group": {
                        "id": subject_group.id,
                        "name": subject_group.name,
                        "code": subject_group.code,
                        "standard": (
                            subject_group.standard.name
                            if subject_group.standard
                            else "Unknown"
                        ),
                    },
                    "academic_class": class_details,
                    "total_students_in_class": class_enrollments.count(),
                    "already_assigned": len(assigned_enrollment_ids),
                    "available_students": available_enrollments,
                    "available_count": len(available_enrollments),
                }
            )

        except SubjectGroup.DoesNotExist:
            return Response(
                {"error": "Subject group not found"}, status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"Error fetching available students: {str(e)}")
            return Response(
                {"error": f"Error fetching available students: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"])
    def class_subject_groups(self, request):
        """
        Get subject groups available for a specific academic class.
        Based on the standard of the academic class.
        GET /api/student-subject-groups/class_subject_groups/?academic_class_id=1
        """
        academic_class_id = request.query_params.get("academic_class_id")

        if not academic_class_id:
            return Response(
                {"error": "academic_class_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get academic class
            academic_class = AcademicClass.objects.get(id=academic_class_id)

            # Get subject groups for the same standard as the academic class
            subject_groups = SubjectGroup.objects.filter(
                standard=academic_class.standard, is_active=True
            )

            serializer = SubjectGroupSerializer(subject_groups, many=True)

            return Response(
                {
                    "academic_class": {
                        "id": academic_class.id,
                        "class_name": f"{academic_class.standard.name if academic_class.standard else 'Unknown'}-{academic_class.section.code if academic_class.section else 'Unknown'}",
                        "standard_id": (
                            academic_class.standard.id
                            if academic_class.standard
                            else None
                        ),
                        "standard_name": (
                            academic_class.standard.name
                            if academic_class.standard
                            else "Unknown"
                        ),
                    },
                    "subject_groups": serializer.data,
                    "count": subject_groups.count(),
                }
            )

        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found"}, status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"Error fetching class subject groups: {str(e)}")
            return Response(
                {"error": f"Error fetching class subject groups: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class StudentSubjectViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing student subject assignments.
    Only shows elective and language subjects.
    """

    queryset = StudentSubject.objects.all()
    serializer_class = StudentSubjectSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]

    search_fields = [
        "enrollment__student__first_name",
        "enrollment__student__last_name",
        "enrollment__student__admission_number",
        "subject__name",
        "subject__code",
    ]

    ordering_fields = ["id", "enrollment__student__first_name", "subject__name"]
    ordering = ["id"]

    def get_queryset(self):
        """
        Get queryset with proper relationships and only elective/language subjects.
        """
        queryset = StudentSubject.objects.select_related(
            "enrollment",
            "enrollment__student",
            "enrollment__academic_class",
            "enrollment__academic_class__standard",
            "enrollment__academic_class__section",
            "enrollment__academic_class__academic_year",
            "subject",
        ).filter(subject__subject_type__in=["elective", "language"])

        # Apply custom filters
        queryset = self.apply_filters(queryset)

        return queryset

    def apply_filters(self, queryset):
        """
        Apply custom filters from query parameters.
        """
        params = self.request.query_params

        # Filter by academic year
        academic_year_id = params.get("academic_year")
        if academic_year_id and academic_year_id != "all":
            queryset = queryset.filter(
                enrollment__academic_class__academic_year_id=academic_year_id
            )

        # Filter by academic class
        academic_class_id = params.get("academic_class") or params.get("academic_class_id")
        if academic_class_id and academic_class_id != "all":
            queryset = queryset.filter(enrollment__academic_class_id=academic_class_id)

        # Filter by subject — by name (subjects exist per-standard, so a name like
        # "Tamil" spans several standard-specific rows) or by a specific id
        subject_name = params.get("subject_name")
        if subject_name and subject_name != "all":
            queryset = queryset.filter(subject__name=subject_name)

        subject_id = params.get("subject") or params.get("subject_id")
        if subject_id and subject_id != "all":
            queryset = queryset.filter(subject_id=subject_id)

        # Filter by subject type
        subject_type = params.get("subject__subject_type") or params.get("subject_type")
        if subject_type and subject_type != "all":
            queryset = queryset.filter(subject__subject_type=subject_type)

        # Filter by status
        status_param = params.get("status")
        if status_param is None and params.get("is_active") is not None:
            status_param = "active" if params.get("is_active") in ("true", "True", True) else "inactive"
        if status_param and status_param != "all":
            if status_param == "active":
                queryset = queryset.filter(is_active=True)
            elif status_param == "inactive":
                queryset = queryset.filter(is_active=False)

        return queryset

    def get_serializer_class(self):
        """
        Use different serializer for creation.
        """
        if self.action == "create":
            return StudentSubjectCreateSerializer
        elif self.action == "list":
            return SimpleStudentSubjectSerializer
        return super().get_serializer_class()

    def create(self, request, *args, **kwargs):
        """
        Create a student subject assignment.
        """
        try:
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            # Check if subject is elective or language
            subject = serializer.validated_data.get("subject")
            if subject.subject_type not in ["elective", "language"]:
                return Response(
                    {"error": "Only elective and language subjects can be assigned"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if already exists
            enrollment = serializer.validated_data.get("enrollment")
            if StudentSubject.objects.filter(
                enrollment=enrollment, subject=subject
            ).exists():
                return Response(
                    {"error": "This student is already assigned to this subject"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)

            # Return the full serializer data
            result_serializer = StudentSubjectSerializer(serializer.instance)
            return Response(
                result_serializer.data, status=status.HTTP_201_CREATED, headers=headers
            )

        except Exception as e:
            logger.error(f"Error creating student subject: {str(e)}")
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["post"])
    def bulk_create(self, request):
        """
        Bulk create student subject assignments.
        """
        serializer = StudentSubjectBulkCreateSerializer(data=request.data)
        if serializer.is_valid():
            enrollment_ids = serializer.validated_data["enrollment_ids"]
            subject_id = serializer.validated_data["subject_id"]
            is_active = serializer.validated_data.get("is_active", True)

            created_subjects = []
            errors = []

            try:
                with transaction.atomic():
                    for enrollment_id in enrollment_ids:
                        try:
                            enrollment = StudentEnrollment.objects.get(id=enrollment_id)

                            # Check if assignment already exists
                            if StudentSubject.objects.filter(
                                enrollment=enrollment, subject_id=subject_id
                            ).exists():
                                errors.append(
                                    {
                                        "enrollment_id": enrollment_id,
                                        "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                                        "error": "Student is already assigned to this subject",
                                    }
                                )
                                continue

                            # Check if subject is elective or language
                            subject = Subject.objects.get(id=subject_id)
                            if subject.subject_type not in ["elective", "language"]:
                                errors.append(
                                    {
                                        "enrollment_id": enrollment_id,
                                        "student_name": f"{enrollment.student.first_name} {enrollment.student.last_name}",
                                        "error": "Subject must be elective or language type",
                                    }
                                )
                                continue

                            # Create student subject
                            student_subject = StudentSubject.objects.create(
                                enrollment=enrollment,
                                subject=subject,
                                is_active=is_active,
                            )
                            created_subjects.append(student_subject)

                        except StudentEnrollment.DoesNotExist:
                            errors.append(
                                {
                                    "enrollment_id": enrollment_id,
                                    "error": "Enrollment not found",
                                }
                            )
                        except Subject.DoesNotExist:
                            errors.append(
                                {
                                    "enrollment_id": enrollment_id,
                                    "error": "Subject not found",
                                }
                            )
                        except Exception as e:
                            errors.append(
                                {"enrollment_id": enrollment_id, "error": str(e)}
                            )

                # Prepare response
                if errors and not created_subjects:
                    return Response(
                        {"errors": errors}, status=status.HTTP_400_BAD_REQUEST
                    )

                result_serializer = StudentSubjectSerializer(
                    created_subjects, many=True
                )

                response_data = {
                    "message": f"Successfully assigned {len(created_subjects)} students to subject",
                    "success_count": len(created_subjects),
                    "error_count": len(errors),
                    "student_subjects": result_serializer.data,
                }

                if errors:
                    response_data["errors"] = errors
                    return Response(response_data, status=status.HTTP_207_MULTI_STATUS)

                return Response(response_data, status=status.HTTP_201_CREATED)

            except Exception as e:
                logger.error(f"Bulk create transaction failed: {str(e)}")
                return Response(
                    {"error": f"Bulk create failed: {str(e)}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # ADD THIS METHOD - IT'S MISSING FROM YOUR CODE
    @action(detail=False, methods=["get"], url_path="available-students")
    def available_students(self, request):
        """
        Get students available for assignment to a subject.
        Only shows students not already assigned to the subject.
        GET /api/student-subjects/available-students/?subject_id=2&academic_class_id=2
        """
        subject_id = request.query_params.get("subject_id")
        academic_class_id = request.query_params.get("academic_class_id")

        if not subject_id or not academic_class_id:
            return Response(
                {"error": "subject_id and academic_class_id are required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get subject
            subject = Subject.objects.get(id=subject_id)

            # Only allow elective and language subjects
            if subject.subject_type not in ["elective", "language"]:
                return Response(
                    {"error": "Only elective and language subjects can be assigned"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Get all active enrollments in the class
            class_enrollments = StudentEnrollment.objects.filter(
                academic_class_id=academic_class_id, is_active=True
            ).select_related("student")

            # Get already assigned enrollment IDs
            assigned_enrollment_ids = StudentSubject.objects.filter(
                subject_id=subject_id, is_active=True
            ).values_list("enrollment_id", flat=True)

            # Filter out already assigned students
            available_enrollments = []
            for enrollment in class_enrollments:
                if enrollment.id not in assigned_enrollment_ids:
                    # Get student name
                    student = enrollment.student
                    student_name = ""
                    if hasattr(student, "full_name") and student.full_name:
                        student_name = student.full_name
                    elif hasattr(student, "first_name"):
                        first_name = student.first_name or ""
                        last_name = getattr(student, "last_name", "") or ""
                        student_name = f"{first_name} {last_name}".strip()

                    # Get admission number
                    admission_number = getattr(student, "admission_number", "") or ""

                    # Get roll number
                    roll_number = (
                        enrollment.roll_number if enrollment.roll_number else ""
                    )

                    available_enrollments.append(
                        {
                            "enrollment_id": enrollment.id,
                            "student_id": enrollment.student.id,
                            "student_name": student_name,
                            "admission_number": admission_number,
                            "roll_number": roll_number,
                        }
                    )

            # Get class details
            class_details = {}
            try:
                academic_class = AcademicClass.objects.get(id=academic_class_id)
                standard_name = (
                    academic_class.standard.name
                    if academic_class.standard
                    else "Unknown"
                )
                section_code = (
                    academic_class.section.code if academic_class.section else "Unknown"
                )
                class_details = {
                    "id": academic_class_id,
                    "class_name": f"{standard_name} - {section_code}",
                    "academic_year": (
                        academic_class.academic_year.name
                        if academic_class.academic_year
                        else "Unknown"
                    ),
                    "standard_id": (
                        academic_class.standard.id if academic_class.standard else None
                    ),
                    "section_id": (
                        academic_class.section.id if academic_class.section else None
                    ),
                }
            except AcademicClass.DoesNotExist:
                class_details = {
                    "id": academic_class_id,
                    "class_name": "Unknown Class",
                    "academic_year": "Unknown",
                    "standard_id": None,
                    "section_id": None,
                }

            return Response(
                {
                    "subject": {
                        "id": subject.id,
                        "name": subject.name,
                        "code": subject.code,
                        "subject_type": subject.subject_type,
                        "subject_type_display": subject.get_subject_type_display(),
                    },
                    "academic_class": class_details,
                    "total_students_in_class": class_enrollments.count(),
                    "already_assigned": len(assigned_enrollment_ids),
                    "available_students": available_enrollments,
                    "available_count": len(available_enrollments),
                }
            )

        except Subject.DoesNotExist:
            return Response(
                {"error": "Subject not found"}, status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"Error fetching available students: {str(e)}")
            return Response(
                {"error": f"Error fetching available students: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    # You might also want to add a bulk update status action
    @action(detail=False, methods=["post"])
    def bulk_update_status(self, request):
        """
        Bulk update student subject status.
        """
        try:
            serializer = StudentSubjectBulkUpdateSerializer(data=request.data)
            if serializer.is_valid():
                student_subject_ids = serializer.validated_data["student_subject_ids"]
                is_active = serializer.validated_data["is_active"]

                updated_count = StudentSubject.objects.filter(
                    id__in=student_subject_ids
                ).update(is_active=is_active)

                return Response(
                    {
                        "message": f"Successfully updated {updated_count} student subject(s)",
                        "updated_count": updated_count,
                        "is_active": is_active,
                    }
                )
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)


from django.db.models import Q
from rest_framework import viewsets, permissions, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db import transaction
import logging

logger = logging.getLogger(__name__)

from .models import SubjectTeacher, Subject, AcademicClass
from people.models import Teacher
from .serializers import (
    SubjectTeacherSerializer,
    SubjectTeacherCreateUpdateSerializer,
    SubjectTeacherBulkCreateSerializer,
    SimpleSubjectTeacherSerializer,
)


class SubjectTeacherViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing subject teacher assignments.
    Links teachers to subjects for specific academic classes.
    """

    queryset = SubjectTeacher.objects.all()
    serializer_class = SubjectTeacherSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]

    # Define search fields
    search_fields = [
        "teacher__first_name",
        "teacher__last_name",
        "teacher__employee_id",
        "subject__name",
        "subject__code",
        "academic_class__standard__name",
        "academic_class__section__code",
        "academic_class__academic_year__name",
    ]

    # Define ordering fields
    ordering_fields = [
        "created_at",
        "teacher__first_name",
        "subject__name",
        "academic_class__standard__order",
    ]
    ordering = ["-created_at"]

    def get_queryset(self):
        """
        Get queryset with proper relationships and filtering.
        """
        queryset = SubjectTeacher.objects.select_related(
            "academic_class",
            "academic_class__standard",
            "academic_class__section",
            "academic_class__academic_year",
            "subject",
            "subject__category",
            "subject__standard",
            "teacher",
        ).all()

        # Apply custom filters
        queryset = self.apply_filters(queryset)

        return queryset

    def apply_filters(self, queryset):
        """
        Apply custom filters from query parameters.
        """
        params = self.request.query_params

        # Filter by academic class
        academic_class_id = params.get("academic_class")
        if academic_class_id and academic_class_id != "all":
            queryset = queryset.filter(academic_class_id=academic_class_id)

        # Filter by subject
        subject_id = params.get("subject")
        if subject_id and subject_id != "all":
            queryset = queryset.filter(subject_id=subject_id)

        # Filter by teacher
        teacher_id = params.get("teacher")
        if teacher_id and teacher_id != "all":
            queryset = queryset.filter(teacher_id=teacher_id)

        # Filter by status
        status_param = params.get("status")
        if status_param and status_param != "all":
            if status_param == "active":
                queryset = queryset.filter(is_active=True)
            elif status_param == "inactive":
                queryset = queryset.filter(is_active=False)

        # Filter by academic year
        academic_year_id = params.get("academic_year")
        if academic_year_id:
            queryset = queryset.filter(
                academic_class__academic_year_id=academic_year_id
            )

        # Filter by standard
        standard_id = params.get("standard")
        if standard_id:
            queryset = queryset.filter(academic_class__standard_id=standard_id)

        # Filter by section
        section_id = params.get("section")
        if section_id:
            queryset = queryset.filter(academic_class__section_id=section_id)

        # Filter by subject category
        category_id = params.get("category")
        if category_id:
            queryset = queryset.filter(subject__category_id=category_id)

        # Filter by subject type
        subject_type = params.get("subject_type")
        if subject_type:
            queryset = queryset.filter(subject__subject_type=subject_type)

        # Search query
        search_query = params.get("search")
        if search_query:
            queryset = queryset.filter(
                Q(teacher__first_name__icontains=search_query)
                | Q(teacher__last_name__icontains=search_query)
                | Q(teacher__employee_id__icontains=search_query)
                | Q(subject__name__icontains=search_query)
                | Q(subject__code__icontains=search_query)
                | Q(academic_class__standard__name__icontains=search_query)
                | Q(academic_class__section__code__icontains=search_query)
                | Q(academic_class__academic_year__name__icontains=search_query)
            )

        return queryset

    def get_serializer_class(self):
        """
        Use different serializer based on action.
        """
        if self.action == "create":
            return SubjectTeacherCreateUpdateSerializer
        elif self.action == "update" or self.action == "partial_update":
            return SubjectTeacherCreateUpdateSerializer
        elif self.action == "list":
            return SimpleSubjectTeacherSerializer
        elif self.action == "bulk_create":
            return SubjectTeacherBulkCreateSerializer
        return super().get_serializer_class()

    def get_permissions(self):
        """
        Customize permissions based on action.
        """
        if self.action in [
            "create",
            "update",
            "partial_update",
            "destroy",
            "bulk_create",
        ]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def create(self, request, *args, **kwargs):
        """
        Create a new subject teacher assignment.
        """
        try:
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            # Validate academic class, subject, and teacher exist and are active
            academic_class = serializer.validated_data.get("academic_class")
            subject = serializer.validated_data.get("subject")
            teacher = serializer.validated_data.get("teacher")

            if not academic_class.is_active:
                return Response(
                    {"error": "Cannot assign to inactive academic class"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not subject.is_active:
                return Response(
                    {"error": "Cannot assign inactive subject"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not teacher.is_active:
                return Response(
                    {"error": "Cannot assign inactive teacher"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if subject belongs to same standard as academic class
            if subject.standard != academic_class.standard:
                return Response(
                    {
                        "error": f"Subject does not belong to standard '{academic_class.standard.name}'"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check for duplicate assignment
            if SubjectTeacher.objects.filter(
                academic_class=academic_class, subject=subject
            ).exists():
                return Response(
                    {
                        "error": "This subject is already assigned to a teacher in this class"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)

            # Return the full serializer data
            result_serializer = SubjectTeacherSerializer(serializer.instance)
            return Response(
                result_serializer.data, status=status.HTTP_201_CREATED, headers=headers
            )

        except Exception as e:
            logger.error(f"Error creating subject teacher: {str(e)}")
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, *args, **kwargs):
        """
        Update a subject teacher assignment.
        PATCH arrives here too (partial_update delegates with partial=True),
        so honor the partial flag instead of forcing full validation.
        """
        try:
            partial = kwargs.pop("partial", False)
            instance = self.get_object()
            serializer = self.get_serializer(
                instance, data=request.data, partial=partial
            )
            serializer.is_valid(raise_exception=True)

            # Validate academic class, subject, and teacher
            academic_class = serializer.validated_data.get(
                "academic_class", instance.academic_class
            )
            subject = serializer.validated_data.get("subject", instance.subject)
            teacher = serializer.validated_data.get("teacher", instance.teacher)

            if not academic_class.is_active:
                return Response(
                    {"error": "Cannot assign to inactive academic class"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not subject.is_active:
                return Response(
                    {"error": "Cannot assign inactive subject"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not teacher.is_active:
                return Response(
                    {"error": "Cannot assign inactive teacher"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if subject belongs to same standard as academic class
            if subject.standard != academic_class.standard:
                return Response(
                    {
                        "error": f"Subject does not belong to standard '{academic_class.standard.name}'"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check for duplicate assignment excluding current instance
            if (
                SubjectTeacher.objects.filter(
                    academic_class=academic_class, subject=subject
                )
                .exclude(id=instance.id)
                .exists()
            ):
                return Response(
                    {
                        "error": "This subject is already assigned to another teacher in this class"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            self.perform_update(serializer)

            # Return the full serializer data
            result_serializer = SubjectTeacherSerializer(serializer.instance)
            return Response(result_serializer.data)

        except Exception as e:
            logger.error(f"Error updating subject teacher: {str(e)}")
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["post"])
    def bulk_create(self, request):
        """
        Bulk create subject teacher assignments.
        Assign one teacher to multiple subjects in a class.
        """
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            try:
                assignments = serializer.save()

                # Return created assignments
                result_serializer = SimpleSubjectTeacherSerializer(
                    assignments, many=True
                )

                return Response(
                    {
                        "message": f"Successfully assigned teacher to {len(assignments)} subjects",
                        "success_count": len(assignments),
                        "assignments": result_serializer.data,
                    },
                    status=status.HTTP_201_CREATED,
                )

            except Exception as e:
                logger.error(f"Bulk create failed: {str(e)}")
                return Response(
                    {"error": f"Bulk create failed: {str(e)}"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get subject teacher assignment statistics.
        """
        queryset = self.filter_queryset(self.get_queryset())

        try:
            # Basic statistics
            total_assignments = queryset.count()
            active_assignments = queryset.filter(is_active=True).count()
            inactive_assignments = total_assignments - active_assignments

            # Group by teacher
            assignments_by_teacher = (
                queryset.values(
                    "teacher__id",
                    "teacher__first_name",
                    "teacher__last_name",
                    "teacher__employee_id",
                )
                .annotate(
                    total=Count("id"),
                    active=Count("id", filter=Q(is_active=True)),
                    inactive=Count("id", filter=Q(is_active=False)),
                )
                .order_by("teacher__first_name", "teacher__last_name")
            )

            # Group by subject
            assignments_by_subject = (
                queryset.values(
                    "subject__id",
                    "subject__name",
                    "subject__code",
                )
                .annotate(
                    total=Count("id"),
                    active=Count("id", filter=Q(is_active=True)),
                    inactive=Count("id", filter=Q(is_active=False)),
                )
                .order_by("subject__name")
            )

            # Group by academic class
            assignments_by_class = (
                queryset.values(
                    "academic_class__id",
                    "academic_class__standard__name",
                    "academic_class__section__code",
                )
                .annotate(
                    total=Count("id"),
                    active=Count("id", filter=Q(is_active=True)),
                    inactive=Count("id", filter=Q(is_active=False)),
                )
                .order_by(
                    "academic_class__standard__name", "academic_class__section__code"
                )
            )

            # Get teacher with most assignments
            top_teacher = (
                queryset.values(
                    "teacher__id",
                    "teacher__first_name",
                    "teacher__last_name",
                )
                .annotate(total_assignments=Count("id"))
                .order_by("-total_assignments")
                .first()
            )

            # Get subject with most teachers
            top_subject = (
                queryset.values(
                    "subject__id",
                    "subject__name",
                )
                .annotate(total_teachers=Count("teacher", distinct=True))
                .order_by("-total_teachers")
                .first()
            )

            return Response(
                {
                    "total_assignments": total_assignments,
                    "active_assignments": active_assignments,
                    "inactive_assignments": inactive_assignments,
                    "assignments_by_teacher": list(assignments_by_teacher),
                    "assignments_by_subject": list(assignments_by_subject),
                    "assignments_by_class": list(assignments_by_class),
                    "top_teacher": top_teacher,
                    "top_subject": top_subject,
                }
            )

        except Exception as e:
            logger.error(f"Error generating statistics: {str(e)}")
            return Response(
                {"error": f"Error generating statistics: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"], url_path="available-subjects")
    def available_subjects(self, request):
        """
        Get subjects available for assignment to a teacher in a specific class.
        GET /api/subject-teachers/available-subjects/?academic_class_id=1&teacher_id=2
        """
        academic_class_id = request.query_params.get("academic_class_id")
        teacher_id = request.query_params.get("teacher_id")

        if not academic_class_id:
            return Response(
                {"error": "academic_class_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get academic class
            academic_class = AcademicClass.objects.get(id=academic_class_id)

            # Get all active subjects for this class's standard
            all_subjects = Subject.objects.filter(
                standard=academic_class.standard, is_active=True
            ).select_related("category", "subject_group")

            # Get already assigned subjects in this class
            assigned_subject_ids = SubjectTeacher.objects.filter(
                academic_class=academic_class
            ).values_list("subject_id", flat=True)

            # If teacher_id is provided, filter out subjects already assigned to this teacher
            if teacher_id:
                teacher_assigned_subject_ids = SubjectTeacher.objects.filter(
                    academic_class=academic_class, teacher_id=teacher_id
                ).values_list("subject_id", flat=True)

                # Combine both sets of assigned subjects
                assigned_subject_ids = set(assigned_subject_ids) | set(
                    teacher_assigned_subject_ids
                )

            # Filter out already assigned subjects
            available_subjects = all_subjects.exclude(id__in=assigned_subject_ids)

            # Format response
            subject_data = []
            for subject in available_subjects:
                subject_data.append(
                    {
                        "subject_id": subject.id,
                        "subject_name": subject.name,
                        "subject_code": subject.code,
                        "subject_type": subject.subject_type,
                        "category_id": (
                            subject.category.id if subject.category else None
                        ),
                        "category_name": (
                            subject.category.name if subject.category else None
                        ),
                        "subject_group_id": (
                            subject.subject_group.id if subject.subject_group else None
                        ),
                        "subject_group_name": (
                            subject.subject_group.name
                            if subject.subject_group
                            else None
                        ),
                    }
                )

            # Get teacher details if teacher_id provided
            teacher_details = None
            if teacher_id:
                try:
                    teacher = Teacher.objects.get(id=teacher_id)
                    teacher_details = {
                        "id": teacher.id,
                        "first_name": teacher.first_name,
                        "last_name": teacher.last_name,
                        "employee_id": teacher.employee_id,
                        "full_name": f"{teacher.first_name} {teacher.last_name}".strip(),
                    }
                except Teacher.DoesNotExist:
                    pass

            return Response(
                {
                    "academic_class": {
                        "id": academic_class.id,
                        "class_name": f"{academic_class.standard.name} - {academic_class.section.code}",
                        "standard_id": academic_class.standard.id,
                        "standard_name": academic_class.standard.name,
                        "section_id": academic_class.section.id,
                        "section_code": academic_class.section.code,
                        "academic_year_id": academic_class.academic_year.id,
                        "academic_year_name": academic_class.academic_year.name,
                    },
                    "teacher": teacher_details,
                    "total_subjects_in_standard": all_subjects.count(),
                    "already_assigned_in_class": len(assigned_subject_ids),
                    "available_subjects": subject_data,
                    "available_count": len(subject_data),
                }
            )

        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Error fetching available subjects: {str(e)}")
            return Response(
                {"error": f"Error fetching available subjects: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"])
    def teacher_workload(self, request):
        """
        Get teacher workload statistics.
        GET /api/subject-teachers/teacher-workload/?teacher_id=1
        """
        teacher_id = request.query_params.get("teacher_id")

        if not teacher_id:
            return Response(
                {"error": "teacher_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get teacher
            teacher = Teacher.objects.get(id=teacher_id)

            # Get all assignments for this teacher
            assignments = SubjectTeacher.objects.filter(
                teacher=teacher, is_active=True
            ).select_related(
                "academic_class",
                "academic_class__standard",
                "academic_class__section",
                "academic_class__academic_year",
                "subject",
            )

            # Group by academic class
            workload_by_class = {}
            for assignment in assignments:
                class_key = f"{assignment.academic_class.standard.name} - {assignment.academic_class.section.code}"
                if class_key not in workload_by_class:
                    workload_by_class[class_key] = {
                        "academic_class_id": assignment.academic_class.id,
                        "class_name": class_key,
                        "academic_year": assignment.academic_class.academic_year.name,
                        "subjects": [],
                        "subject_count": 0,
                    }

                workload_by_class[class_key]["subjects"].append(
                    {
                        "subject_id": assignment.subject.id,
                        "subject_name": assignment.subject.name,
                        "subject_code": assignment.subject.code,
                        "subject_type": assignment.subject.subject_type,
                    }
                )
                workload_by_class[class_key]["subject_count"] += 1

            # Convert to list
            workload_list = list(workload_by_class.values())

            # Calculate totals
            total_classes = len(workload_list)
            total_subjects = assignments.count()

            return Response(
                {
                    "teacher": {
                        "id": teacher.id,
                        "first_name": teacher.first_name,
                        "last_name": teacher.last_name,
                        "employee_id": teacher.employee_id,
                        "full_name": f"{teacher.first_name} {teacher.last_name}".strip(),
                    },
                    "total_classes": total_classes,
                    "total_subjects": total_subjects,
                    "workload": workload_list,
                }
            )

        except Teacher.DoesNotExist:
            return Response(
                {"error": "Teacher not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Error fetching teacher workload: {str(e)}")
            return Response(
                {"error": f"Error fetching teacher workload: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"])
    def class_teachers(self, request):
        """
        Get all teachers assigned to subjects in a specific class.
        GET /api/subject-teachers/class-teachers/?academic_class_id=1
        """
        academic_class_id = request.query_params.get("academic_class_id")

        if not academic_class_id:
            return Response(
                {"error": "academic_class_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get academic class
            academic_class = AcademicClass.objects.get(id=academic_class_id)

            # Get all subject teacher assignments for this class
            assignments = SubjectTeacher.objects.filter(
                academic_class=academic_class, is_active=True
            ).select_related(
                "teacher",
                "subject",
                "subject__category",
            )

            # Group by teacher
            teachers_dict = {}
            for assignment in assignments:
                teacher_id = assignment.teacher.id
                if teacher_id not in teachers_dict:
                    teachers_dict[teacher_id] = {
                        "teacher_id": assignment.teacher.id,
                        "first_name": assignment.teacher.first_name,
                        "last_name": assignment.teacher.last_name,
                        "employee_id": assignment.teacher.employee_id,
                        "full_name": f"{assignment.teacher.first_name} {assignment.teacher.last_name}".strip(),
                        "subjects": [],
                        "subject_count": 0,
                    }

                teachers_dict[teacher_id]["subjects"].append(
                    {
                        "subject_id": assignment.subject.id,
                        "subject_name": assignment.subject.name,
                        "subject_code": assignment.subject.code,
                        "subject_type": assignment.subject.subject_type,
                        "category_name": (
                            assignment.subject.category.name
                            if assignment.subject.category
                            else None
                        ),
                    }
                )
                teachers_dict[teacher_id]["subject_count"] += 1

            # Convert to list
            teachers_list = list(teachers_dict.values())

            return Response(
                {
                    "academic_class": {
                        "id": academic_class.id,
                        "class_name": f"{academic_class.standard.name} - {academic_class.section.code}",
                        "standard": academic_class.standard.name,
                        "section": academic_class.section.code,
                        "academic_year": academic_class.academic_year.name,
                    },
                    "total_teachers": len(teachers_list),
                    "teachers": teachers_list,
                }
            )

        except AcademicClass.DoesNotExist:
            return Response(
                {"error": "Academic class not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Error fetching class teachers: {str(e)}")
            return Response(
                {"error": f"Error fetching class teachers: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"])
    def teacher_subjects(self, request):
        """
        Get all subjects assigned to a teacher.
        GET /api/subject-teachers/teacher-subjects/?teacher_id=1
        """
        teacher_id = request.query_params.get("teacher_id")

        if not teacher_id:
            return Response(
                {"error": "teacher_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Get teacher
            teacher = Teacher.objects.get(id=teacher_id)

            # Get all subject teacher assignments for this teacher
            assignments = (
                SubjectTeacher.objects.filter(teacher=teacher, is_active=True)
                .select_related(
                    "subject",
                    "academic_class",
                    "academic_class__standard",
                    "academic_class__section",
                    "academic_class__academic_year",
                )
                .order_by("subject__name")
            )

            # Get unique subjects
            subjects_dict = {}
            for assignment in assignments:
                subject_id = assignment.subject.id
                if subject_id not in subjects_dict:
                    subjects_dict[subject_id] = {
                        "subject_id": assignment.subject.id,
                        "subject_name": assignment.subject.name,
                        "subject_code": assignment.subject.code,
                        "subject_type": assignment.subject.subject_type,
                        "classes": [],
                        "class_count": 0,
                    }

                subjects_dict[subject_id]["classes"].append(
                    {
                        "academic_class_id": assignment.academic_class.id,
                        "class_name": f"{assignment.academic_class.standard.name} - {assignment.academic_class.section.code}",
                        "academic_year": assignment.academic_class.academic_year.name,
                    }
                )
                subjects_dict[subject_id]["class_count"] += 1

            # Convert to list
            subjects_list = list(subjects_dict.values())

            return Response(
                {
                    "teacher": {
                        "id": teacher.id,
                        "first_name": teacher.first_name,
                        "last_name": teacher.last_name,
                        "employee_id": teacher.employee_id,
                        "full_name": f"{teacher.first_name} {teacher.last_name}".strip(),
                    },
                    "total_subjects": len(subjects_list),
                    "subjects": subjects_list,
                }
            )

        except Teacher.DoesNotExist:
            return Response(
                {"error": "Teacher not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Error fetching teacher subjects: {str(e)}")
            return Response(
                {"error": f"Error fetching teacher subjects: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["post"])
    def bulk_update_status(self, request):
        """
        Bulk update subject teacher assignment status.
        """
        subject_teacher_ids = request.data.get("subject_teacher_ids", [])
        is_active = request.data.get("is_active")

        if not subject_teacher_ids or not isinstance(subject_teacher_ids, list):
            return Response(
                {"error": "subject_teacher_ids list is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if is_active is None:
            return Response(
                {"error": "is_active is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            with transaction.atomic():
                updated_count = SubjectTeacher.objects.filter(
                    id__in=subject_teacher_ids
                ).update(is_active=is_active)

            return Response(
                {
                    "message": f"Successfully updated {updated_count} assignment(s)",
                    "updated_count": updated_count,
                    "is_active": is_active,
                }
            )

        except Exception as e:
            logger.error(f"Bulk update failed: {str(e)}")
            return Response(
                {"error": f"Bulk update failed: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["post"])
    def toggle_active(self, request, pk=None):
        """
        Toggle active status of a subject teacher assignment.
        """
        try:
            assignment = self.get_object()
            assignment.is_active = not assignment.is_active
            assignment.save()

            serializer = self.get_serializer(assignment)
            return Response(
                {
                    "message": f"Assignment {'activated' if assignment.is_active else 'deactivated'} successfully",
                    "assignment": serializer.data,
                    "is_active": assignment.is_active,
                }
            )

        except SubjectTeacher.DoesNotExist:
            return Response(
                {"error": "Assignment not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Toggle active failed: {str(e)}")
            return Response(
                {"error": f"Toggle active failed: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )
