# people/views.py
import logging
from rest_framework import viewsets, permissions, status, filters, serializers
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from django.db.models import Q, Count
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth import get_user_model
from django.db import transaction
from django.contrib.auth.hashers import make_password

from .models import Teacher, Parent, Student, StudentParent
from .serializers import (
    TeacherSerializer,
    ParentSerializer,
    StudentSerializer,
    StudentParentSerializer,
    SimpleTeacherSerializer,
    SimpleParentSerializer,
    SimpleStudentSerializer,
)

logger = logging.getLogger(__name__)
User = get_user_model()

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100
    
class BasePeopleViewSet(viewsets.ModelViewSet):
    """Base ViewSet with common multi-tenant logic"""

    def _log_step(self, step, message, level="info"):
        """Helper method for structured logging"""
        log_method = getattr(logger, level)
        log_method(f"{'='*60}")
        log_method(f"STEP {step}: {message}")
        log_method(f"{'='*60}")

    def _create_master_user(self, user_data, role="USER"):
        """
        Create user in master database
        Returns: (success, user_id or error_message)
        """
        try:
            self._log_step(
                "A", f"Creating {role} in MASTER DB: {user_data.get('email')}"
            )

            # Prepare user data
            username = user_data.get("username", user_data.get("email"))
            email = user_data.get("email")
            password = user_data.get("password")
            first_name = user_data.get("first_name", "")
            last_name = user_data.get("last_name", "")

            # Check if user already exists
            if User.objects.using("default").filter(email=email).exists():
                return (
                    False,
                    f"User with email {email} already exists in master database",
                )

            # Try to create user using the correct manager method
            try:
                # Method 1: Use db_manager to get the manager for the 'default' database
                user = User.objects.db_manager("default").create_user(
                    username=username,
                    email=email,
                    password=password,
                    first_name=first_name,
                    last_name=last_name,
                    is_staff=(role == "TEACHER"),  # Teachers are staff
                    is_superuser=False,
                    is_active=True,
                )
            except AttributeError:
                # Method 2: Fallback - manually create the user if create_user doesn't exist
                self._log_step("A", "Using manual user creation fallback")
                user = User(
                    username=username,
                    email=email,
                    password=make_password(password),
                    first_name=first_name,
                    last_name=last_name,
                    is_staff=(role == "TEACHER"),
                    is_superuser=False,
                    is_active=True,
                )
                user.save(using="default")

            self._log_step("A", f"✅ MASTER {role} created: {user.id}")
            return True, user.id

        except Exception as e:
            error_msg = f"Failed to create MASTER {role}: {str(e)}"
            self._log_step("A", f"❌ {error_msg}", "error")
            return False, error_msg

    def _create_user_profile(self, user_id, school, role, external_id=None):
        """
        Create UserProfile in master database to link user to school
        """
        try:
            from master_admin.models import UserProfile
            from django.utils import timezone as tz

            self._log_step(
                "B", f"Creating UserProfile in MASTER DB for {role} - User: {user_id}"
            )

            user = User.objects.using("default").get(id=user_id)

            # Prepare profile data
            profile_data = {
                "external_id": (
                    external_id if external_id else str(user_id)
                ),
                "is_active": True,
                "is_primary_contact": False,
                "email_notifications": True,
                "sms_notifications": True,
                "language": "en",
                "timezone": "UTC",
                "created_at": tz.now(),
            }

            # Prepare metadata to store role and external IDs
            metadata = {
                "role": role,
                "master_user_id": user_id,
                "created_at": str(tz.now()),
                "source": "school_management_system",
            }

            # Store role-specific external ID in metadata
            if role == "STUDENT":
                metadata["external_student_id"] = (
                    external_id if external_id else str(user_id)
                )
            elif role == "TEACHER":
                metadata["external_teacher_id"] = (
                    external_id if external_id else str(user_id)
                )
            elif role == "PARENT":
                metadata["external_parent_id"] = (
                    external_id if external_id else str(user_id)
                )

            profile_data["metadata"] = metadata

            # Create or update UserProfile
            user_profile, created = UserProfile.objects.using(
                "default"
            ).update_or_create(user=user, school=school, defaults=profile_data)

            if created:
                self._log_step(
                    "B",
                    f"✅ UserProfile created: {user_profile.id} (User: {user_id}, Role: {role})",
                )
            else:
                self._log_step(
                    "B",
                    f"✅ UserProfile updated: {user_profile.id} (User: {user_id}, Role: {role})",
                )

            return True

        except ImportError:
            self._log_step(
                "B", "⚠️ UserProfile model not found in master_admin", "warning"
            )
            return False
        except Exception as e:
            self._log_step("B", f"❌ Failed to create UserProfile: {str(e)}", "error")
            return False

    def _rollback_master_user(self, user_id):
        """Rollback master user if school record creation fails"""
        try:
            if user_id:
                User.objects.using("default").filter(id=user_id).delete()
                self._log_step("ROLLBACK", f"🧹 Rolled back MASTER user: {user_id}")
        except Exception as e:
            self._log_step("ROLLBACK", f"❌ Failed to rollback user: {str(e)}", "error")

    def _sync_to_master_db(self, external_user_id, first_name=None, last_name=None, email=None, password=None):
        """Sync identity fields to master DB auth.User. Always called on update — silent on failure."""
        try:
            user = User.objects.using("default").get(id=external_user_id)
            changed = False
            if first_name is not None:
                user.first_name = first_name
                changed = True
            if last_name is not None:
                user.last_name = last_name
                changed = True
            if email:
                already_taken = (
                    User.objects.using("default")
                    .exclude(id=external_user_id)
                    .filter(email=email)
                    .exists()
                )
                if not already_taken:
                    user.email = email
                    user.username = email
                    changed = True
                else:
                    self._log_step("MASTER_SYNC", f"⚠️ Email {email} already taken in master DB — skipping", "warning")
            if password:
                user.set_password(password)
                changed = True
            if changed:
                user.save(using="default")
                self._log_step("MASTER_SYNC", f"✅ Master DB synced for user: {external_user_id}")
        except User.DoesNotExist:
            self._log_step("MASTER_SYNC", f"⚠️ Master user not found: {external_user_id}", "warning")
        except Exception as e:
            self._log_step("MASTER_SYNC", f"⚠️ Sync failed: {str(e)}", "warning")


class TeacherViewSet(BasePeopleViewSet):
    """
    ViewSet for managing teachers with multi-tenant support
    """

    queryset = Teacher.objects.all()  # Show all teachers (active and inactive)
    serializer_class = TeacherSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = ["employment_type", "is_active", "gender"]
    search_fields = ["first_name", "last_name", "email", "employee_id", "qualification"]
    ordering_fields = ["first_name", "last_name", "date_of_joining", "created_at"]
    ordering = ["first_name", "last_name"]

    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):
        # Base queryset includes all teachers (active and inactive)
        queryset = Teacher.objects.all()

        # Filter by is_active if explicitly requested
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            if is_active.lower() == 'true':
                queryset = queryset.filter(is_active=True)
            elif is_active.lower() == 'false':
                queryset = queryset.filter(is_active=False)
        # If no filter is applied, show all teachers (both active and inactive)

        # ── Filter by gender if explicitly requested ── NEW
        gender = self.request.query_params.get('gender')
        if gender:
            queryset = queryset.filter(gender=gender.upper())

        # Search by name or email — tokenize so a full "firstname lastname"
        # query matches across the separate first_name/last_name fields.
        search = self.request.query_params.get("search", None)
        if search and search.strip():
            for token in search.split():
                queryset = queryset.filter(
                    Q(first_name__icontains=token)
                    | Q(last_name__icontains=token)
                    | Q(email__icontains=token)
                    | Q(employee_id__icontains=token)
                )

        return queryset.order_by("first_name", "last_name")

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """
        Create Teacher with optional Master DB user - FIXED VERSION
        """
        self._log_step("START", "TEACHER CREATION PROCESS")

        try:
            # Get data
            data = request.data.copy()

            # Debug: Log what we received
            self._log_step("DEBUG", f"Raw data keys: {list(data.keys())}")

            # Extract user creation info - CRITICAL FIX
            # FormData sends strings, not booleans
            create_user_account_str = data.get("create_user_account", "false")
            create_user_account = create_user_account_str.lower() == "true"

            user_email = data.get("user_email", None)
            user_password = data.get("user_password", None)

            # Debug user creation fields
            self._log_step(
                "USER_CREATION", f"create_user_account (raw): {create_user_account_str}"
            )
            self._log_step(
                "USER_CREATION", f"create_user_account (parsed): {create_user_account}"
            )
            self._log_step("USER_CREATION", f"user_email: {user_email}")
            self._log_step(
                "USER_CREATION",
                f"user_password: {'[PRESENT]' if user_password else '[MISSING]'}",
            )

            external_user_id = None

            # Step 1: Create User in Master DB if requested
            if create_user_account and user_email and user_password:
                # First check if email already exists in master DB
                self._log_step(
                    "MASTER_CHECK",
                    f"Checking if email exists in master DB: {user_email}",
                )

                if User.objects.using("default").filter(email=user_email).exists():
                    return Response(
                        {
                            "error": f"User with email {user_email} already exists in master database"
                        },
                        status=status.HTTP_400_BAD_REQUEST,
                    )

                user_data = {
                    "email": user_email,
                    "password": user_password,
                    "first_name": data.get("first_name", ""),
                    "last_name": data.get("last_name", ""),
                    "username": user_email,
                }

                success, result = self._create_master_user(user_data, "TEACHER")
                if not success:
                    return Response(
                        {"error": result}, status=status.HTTP_400_BAD_REQUEST
                    )
                external_user_id = result
                self._log_step(
                    "MASTER_CREATE", f"✅ Created master user: {external_user_id}"
                )

            # Step 2: Validate Teacher data
            self._log_step("VALIDATION", "Validating Teacher data")

            # Clean up data for serializer - remove user creation fields
            serializer_data = data.copy()

            # IMPORTANT: Remove ONLY the user creation fields, not all fields
            fields_to_remove = [
                "create_user_account",
                "user_email",
                "user_password",
                "confirm_password",
            ]
            for field in fields_to_remove:
                if field in serializer_data:
                    del serializer_data[field]

            serializer = self.get_serializer(data=serializer_data)

            try:
                serializer.is_valid(raise_exception=True)
            except serializers.ValidationError as e:
                self._log_step(
                    "VALIDATION_ERROR", f"Serializer validation failed: {e.detail}"
                )
                # Handle email validation error specifically
                if "email" in e.detail and "already exists" in str(e.detail["email"]):
                    # Check if it's school DB error
                    email_to_check = data.get("email")
                    if (
                        email_to_check
                        and Teacher.objects.filter(email=email_to_check).exists()
                    ):
                        return Response(
                            {
                                "error": f"Email {email_to_check} already exists in school database"
                            },
                            status=status.HTTP_400_BAD_REQUEST,
                        )
                # Re-raise other validation errors
                raise e

            # Step 3: Create Teacher in School DB
            self._log_step("CREATE_SCHOOL", "Creating Teacher in SCHOOL DB")

            # Add external_user_id to validated data if created
            if external_user_id:
                serializer.validated_data["external_user_id"] = external_user_id
                self._log_step(
                    "LINKING", f"Linking teacher to master user: {external_user_id}"
                )

            # Save teacher (goes to school DB via router)
            teacher = serializer.save()
            self._log_step("CREATE_SCHOOL", f"✅ TEACHER created: {teacher.id}")

            # Step 4: Create UserProfile in Master DB
            if external_user_id and hasattr(request, "school"):
                profile_created = self._create_user_profile(
                    user_id=external_user_id,
                    school=request.school,
                    role="TEACHER",
                    external_id=teacher.id,
                )
                if profile_created:
                    self._log_step(
                        "PROFILE", f"✅ UserProfile created for teacher: {teacher.id}"
                    )
                else:
                    self._log_step(
                        "PROFILE",
                        f"⚠️ UserProfile creation failed for teacher: {teacher.id}",
                    )

            # Step 5: Log successful completion
            self._log_step(
                "END",
                f"TEACHER CREATION COMPLETE - ID: {teacher.id}, Master ID: {external_user_id or 'None'}",
            )

            headers = self.get_success_headers(serializer.data)
            return Response(
                serializer.data, status=status.HTTP_201_CREATED, headers=headers
            )

        except Exception as e:
            self._log_step("ERROR", f"Teacher creation failed: {str(e)}", "error")
            # Rollback master user if created
            if "external_user_id" in locals() and external_user_id:
                self._rollback_master_user(external_user_id)

            return Response(
                {"error": f"Failed to create teacher: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @transaction.atomic
    def update(self, request, *args, **kwargs):
        """
        Update Teacher.
        Supports `remove_profile_image=true` in the request payload to clear
        an existing profile picture from both the filesystem and the database.
        QueryDict.pop() returns lists for multipart/form-data — all flags are
        unwrapped before use.
        """
        self._log_step("START", "TEACHER UPDATE PROCESS")

        partial = kwargs.pop("partial", False)
        instance = self.get_object()

        try:
            data = request.data.copy()

            # ── Handle profile image removal ───────────────────────────────────
            # QueryDict.pop() returns a list for multipart/form-data — unwrap it.
            remove_profile_image_raw = data.pop("remove_profile_image", ["false"])
            if isinstance(remove_profile_image_raw, list):
                remove_profile_image_raw = remove_profile_image_raw[0] if remove_profile_image_raw else "false"
            if isinstance(remove_profile_image_raw, str):
                remove_profile_image = remove_profile_image_raw.lower() == "true"
            else:
                remove_profile_image = bool(remove_profile_image_raw)

            if remove_profile_image and instance.profile_image:
                self._log_step(
                    "IMG_REMOVE",
                    f"Removing profile image for teacher: {instance.id}",
                )
                # Delete the file from storage
                instance.profile_image.delete(save=False)
                # Clear the field so the serializer persists null
                instance.profile_image = None

            # ── Strip user-account fields (not updated via this endpoint) ──────
            for field in ("user_email", "create_user_account", "user_password", "confirm_password"):
                data.pop(field, None)

            # ── Update Teacher in School DB ────────────────────────────────────
            self._log_step("1", f"Updating TEACHER in SCHOOL DB: {instance.id}")

            serializer = self.get_serializer(instance, data=data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)

            # ── Auto-sync identity fields to master DB ─────────────────────────
            if instance.external_user_id:
                updated = serializer.instance
                self._sync_to_master_db(
                    external_user_id=instance.external_user_id,
                    first_name=updated.first_name,
                    last_name=updated.last_name,
                    email=updated.email,
                )

            self._log_step("END", f"TEACHER UPDATE COMPLETE - ID: {instance.id}")
            return Response(serializer.data)

        except Exception as e:
            self._log_step("ERROR", f"Teacher update failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to update teacher: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )
            
class StudentViewSet(BasePeopleViewSet):
    """
    ViewSet for managing students with multi-tenant support.

    Changes vs original:
    - create(): user account is ALWAYS created (no opt-in toggle).
      The frontend sends user_email + user_password every time.
    - update(): honours the `remove_profile_image` flag sent by the
      frontend to clear an existing profile picture from storage & DB.
      If no new file is uploaded and remove_profile_image is not sent,
      the existing profile image is preserved untouched.
    - get_queryset(): supports filtering by is_active, gender, and search.
    """

    queryset = Student.objects.all()  # Remove filter to show all students
    serializer_class = StudentSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = ["gender", "blood_group", "is_active"]
    search_fields = [
        "first_name",
        "last_name",
        "student_id",
        "roll_number",
        "admission_number",
    ]
    ordering_fields = ["first_name", "last_name", "admission_date", "created_at"]
    ordering = ["last_name", "first_name"]
    pagination_class = StandardResultsSetPagination
    
    def get_permissions(self):
        if self.action in [
            "create",
            "update",
            "partial_update",
            "destroy",
            "bulk_upload",
        ]:
            return [permissions.IsAuthenticated(), permissions.IsAdminUser()]
        return [permissions.IsAuthenticated()]

    def get_queryset(self):
        # Base queryset includes all students (active and inactive)
        queryset = Student.objects.all()

        # Filter by is_active if explicitly requested
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            if is_active.lower() == 'true':
                queryset = queryset.filter(is_active=True)
            elif is_active.lower() == 'false':
                queryset = queryset.filter(is_active=False)
        # If no filter is applied, show all students (both active and inactive)

        # ── Filter by gender if explicitly requested ── NEW
        gender = self.request.query_params.get('gender')
        if gender:
            queryset = queryset.filter(gender=gender.upper())

        # Search by name or ID — tokenize so "harini subramanian" (first +
        # last name across two separate fields) matches, not just a single
        # field containing the whole typed string.
        search = self.request.query_params.get("search", None)
        if search and search.strip():
            for token in search.split():
                queryset = queryset.filter(
                    Q(first_name__icontains=token)
                    | Q(last_name__icontains=token)
                    | Q(student_id__icontains=token)
                    | Q(roll_number__icontains=token)
                    | Q(admission_number__icontains=token)
                )

        return queryset.order_by("last_name", "first_name")

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """
        Create Student and ALWAYS create a Master DB user account.
        The frontend sends user_email and user_password every time
        (no toggle needed).
        """
        self._log_step("START", "STUDENT CREATION PROCESS")

        try:
            data = request.data.copy()

            # ── User account – always created ──────────────────────────────────
            user_email = data.get("user_email") or data.get("personal_email")
            user_password = data.get("user_password")

            if not user_email:
                return Response(
                    {"error": "An email address is required to create the student user account."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not user_password:
                return Response(
                    {"error": "A password is required to create the student user account."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check for duplicate email in master DB
            if User.objects.using("default").filter(email=user_email).exists():
                return Response(
                    {"error": f"User with email {user_email} already exists in master database"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            user_data = {
                "email": user_email,
                "password": user_password,
                "first_name": data.get("first_name", ""),
                "last_name": data.get("last_name", ""),
                "username": user_email,
            }

            success, result = self._create_master_user(user_data, "STUDENT")
            if not success:
                return Response({"error": result}, status=status.HTTP_400_BAD_REQUEST)

            external_user_id = result
            self._log_step("MASTER_CREATE", f"✅ Created master user: {external_user_id}")

            # ── Validate Student data ──────────────────────────────────────────
            self._log_step("1", "Validating Student data")

            # Strip user-account fields before passing to serializer
            serializer_data = data.copy()
            for field in ("user_email", "create_user_account", "user_password", "confirm_password"):
                serializer_data.pop(field, None)

            serializer = self.get_serializer(data=serializer_data)
            serializer.is_valid(raise_exception=True)

            # Inject master user ID
            serializer.validated_data["external_user_id"] = external_user_id

            # ── Create Student in School DB ────────────────────────────────────
            self._log_step("2", "Creating Student in SCHOOL DB")
            student = serializer.save()
            self._log_step("2", f"✅ STUDENT created: {student.id}")

            # ── Create UserProfile in Master DB ───────────────────────────────
            if hasattr(request, "school"):
                self._create_user_profile(
                    user_id=external_user_id,
                    school=request.school,
                    role="STUDENT",
                    external_id=student.id,
                )

            self._log_step("END", f"STUDENT CREATION COMPLETE - ID: {student.id}")

            headers = self.get_success_headers(serializer.data)
            return Response(
                serializer.data, status=status.HTTP_201_CREATED, headers=headers
            )

        except Exception as e:
            self._log_step("ERROR", f"Student creation failed: {str(e)}", "error")
            # Rollback master user if it was created before the error
            if "external_user_id" in locals() and external_user_id:
                self._rollback_master_user(external_user_id)

            return Response(
                {"error": f"Failed to create student: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @transaction.atomic
    def update(self, request, *args, **kwargs):
        """
        Update Student.

        Profile image handling (three cases):
          1. New file uploaded  → request.FILES has 'profile_image' → use it
          2. Remove requested   → 'remove_profile_image' == 'true'  → delete & clear
          3. No change          → neither sent                       → leave existing image alone

        Also supports optionally updating the linked Master DB user via
        `update_user=true`, `user_email`, and `user_password`.
        """
        self._log_step("START", "STUDENT UPDATE PROCESS")

        partial = kwargs.pop("partial", False)
        instance = self.get_object()

        try:
            data = request.data.copy()

            # ── Handle profile image removal ───────────────────────────────────
            remove_profile_image_raw = data.pop("remove_profile_image", None)

            if remove_profile_image_raw is not None:
                # Frontend explicitly sent the flag — normalise to bool
                if isinstance(remove_profile_image_raw, str):
                    remove_profile_image = remove_profile_image_raw.lower() == "true"
                else:
                    remove_profile_image = bool(remove_profile_image_raw)
            else:
                # Flag was not sent at all → do not touch the image
                remove_profile_image = False

            if remove_profile_image and instance.profile_image:
                self._log_step(
                    "IMG_REMOVE",
                    f"Removing profile image for student: {instance.id}",
                )
                # Delete the file from storage
                instance.profile_image.delete(save=False)
                # Clear the field on the instance so the serializer saves it as null
                instance.profile_image = None

            # ── FIX: If no new image file was uploaded, strip 'profile_image'
            #    from the data dict entirely so DRF's serializer does not
            #    overwrite the existing image with an empty/null value.
            if 'profile_image' not in request.FILES:
                data.pop('profile_image', None)
                self._log_step(
                    "IMG_PRESERVE",
                    f"No new image uploaded — preserving existing profile image for student: {instance.id}",
                )

            # ── Extract optional password change ───────────────────────────────
            data.pop("update_user", None)
            user_password = data.pop("user_password", None)
            data.pop("user_email", None)

            # ── Update Student in School DB ────────────────────────────────────
            self._log_step("1", f"Updating STUDENT in SCHOOL DB: {instance.id}")

            serializer = self.get_serializer(instance, data=data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)

            # ── Auto-sync identity fields to master DB ─────────────────────────
            if instance.external_user_id:
                updated = serializer.instance
                self._sync_to_master_db(
                    external_user_id=instance.external_user_id,
                    first_name=updated.first_name,
                    last_name=updated.last_name,
                    email=updated.personal_email,
                    password=user_password or None,
                )

            self._log_step("END", f"STUDENT UPDATE COMPLETE - ID: {instance.id}")
            return Response(serializer.data)

        except Exception as e:
            self._log_step("ERROR", f"Student update failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to update student: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["post"], url_path="bulk-upload")
    @transaction.atomic
    def bulk_upload(self, request):
        """
        Bulk upload students from Excel file with proper field mapping
        Supports Master DB user creation similar to single student creation
        Expected Excel columns (additional columns for user creation):
        - student_id, roll_number, admission_number, first_name, last_name,
          date_of_birth, gender, blood_group, personal_email, phone,
          admission_date, current_class, section, address, city, state,
          pincode, emergency_contact_name, emergency_contact_phone,
          create_user_account (True/False), user_email, user_password
        """
        self._log_step("START", "STUDENT BULK UPLOAD PROCESS")

        if "file" not in request.FILES:
            return Response(
                {"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST
            )

        excel_file = request.FILES["file"]

        # Validate file extension
        if not excel_file.name.endswith((".xlsx", ".xls")):
            return Response(
                {"error": "Only Excel files (.xlsx, .xls) are allowed"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            # Read Excel file
            df = pd.read_excel(excel_file)

            # Define required and optional fields
            required_fields = ["first_name", "last_name", "date_of_birth", "gender"]
            optional_fields = [
                "student_id",
                "roll_number",
                "admission_number",
                "blood_group",
                "personal_email",
                "phone",
                "admission_date",
                "current_class",
                "section",
                "address",
                "city",
                "state",
                "pincode",
                "emergency_contact_name",
                "emergency_contact_phone",
            ]

            # User creation fields
            user_fields = ["create_user_account", "user_email", "user_password"]

            # Check required columns
            missing_columns = []
            for field in required_fields:
                if field not in df.columns:
                    missing_columns.append(field)

            if missing_columns:
                return Response(
                    {
                        "error": f"Missing required columns in Excel: {', '.join(missing_columns)}",
                        "required_columns": required_fields,
                        "optional_columns": optional_fields,
                        "user_fields": user_fields,
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Track emails to check for duplicates in master DB
            emails_to_create = []
            email_user_map = {}  # Maps email to row data for user creation

            # First pass: Collect emails and check for duplicates
            for index, row in df.iterrows():
                create_user_account = self._parse_boolean(
                    row.get("create_user_account", False)
                )
                user_email = row.get("user_email") or row.get("personal_email")

                if create_user_account and user_email and not pd.isna(user_email):
                    user_email = str(user_email).strip().lower()

                    # Check for duplicate in current batch
                    if user_email in email_user_map:
                        return Response(
                            {
                                "error": f"Duplicate email '{user_email}' found in Excel file at rows {email_user_map[user_email]['row']} and {index + 2}"
                            },
                            status=status.HTTP_400_BAD_REQUEST,
                        )

                    # Check for duplicate in master DB
                    if User.objects.using("default").filter(email=user_email).exists():
                        return Response(
                            {
                                "error": f"User with email '{user_email}' already exists in master database (row {index + 2})"
                            },
                            status=status.HTTP_400_BAD_REQUEST,
                        )

                    emails_to_create.append(user_email)
                    email_user_map[user_email] = {"row": index + 2, "data": row}

            # Initialize results
            results = {
                "total_records": len(df),
                "successful": 0,
                "failed": 0,
                "errors": [],
                "created_students": [],
                "users_created": 0,
                "user_creation_errors": [],
            }

            # Process each row
            for index, row in df.iterrows():
                row_num = index + 2  # +2 because Excel is 1-indexed and header is row 1

                try:
                    # Prepare student data
                    student_data = {}

                    # Map required fields
                    student_data["first_name"] = str(row.get("first_name", "")).strip()
                    student_data["last_name"] = str(row.get("last_name", "")).strip()

                    # Handle date_of_birth
                    dob = row.get("date_of_birth")
                    if pd.isna(dob):
                        results["errors"].append(
                            f"Row {row_num}: date_of_birth is required"
                        )
                        results["failed"] += 1
                        continue

                    # Convert date format
                    if isinstance(dob, pd.Timestamp):
                        student_data["date_of_birth"] = dob.date()
                    elif isinstance(dob, datetime):
                        student_data["date_of_birth"] = dob.date()
                    elif isinstance(dob, str):
                        try:
                            student_data["date_of_birth"] = datetime.strptime(
                                dob, "%Y-%m-%d"
                            ).date()
                        except ValueError:
                            try:
                                student_data["date_of_birth"] = datetime.strptime(
                                    dob, "%d/%m/%Y"
                                ).date()
                            except ValueError:
                                results["errors"].append(
                                    f"Row {row_num}: Invalid date format for date_of_birth. Use YYYY-MM-DD or DD/MM/YYYY"
                                )
                                results["failed"] += 1
                                continue
                    else:
                        results["errors"].append(
                            f"Row {row_num}: Invalid date_of_birth format"
                        )
                        results["failed"] += 1
                        continue

                    # Handle gender
                    gender = str(row.get("gender", "")).strip().upper()
                    valid_genders = ["MALE", "FEMALE", "OTHER"]
                    if gender not in valid_genders:
                        results["errors"].append(
                            f"Row {row_num}: Invalid gender '{gender}'. Must be one of: {', '.join(valid_genders)}"
                        )
                        results["failed"] += 1
                        continue
                    student_data["gender"] = gender

                    # Extract user creation info (same logic as single create)
                    create_user_account = self._parse_boolean(
                        row.get("create_user_account", False)
                    )
                    user_email = row.get("user_email")
                    user_password = row.get("user_password")

                    # If user_email not provided, use personal_email
                    if not user_email or pd.isna(user_email):
                        user_email = row.get("personal_email")

                    external_user_id = None

                    # Create User in Master DB if requested (same as single create)
                    if (
                        create_user_account
                        and user_email
                        and user_password
                        and not pd.isna(user_email)
                        and not pd.isna(user_password)
                    ):
                        user_email = str(user_email).strip().lower()
                        user_password = str(user_password).strip()

                        self._log_step(
                            f"ROW {row_num}", f"Creating MASTER user for: {user_email}"
                        )

                        user_data = {
                            "email": user_email,
                            "password": user_password,
                            "first_name": student_data["first_name"],
                            "last_name": student_data["last_name"],
                            "username": user_email,
                        }

                        success, result = self._create_master_user(user_data, "STUDENT")
                        if not success:
                            results["errors"].append(
                                f"Row {row_num}: Failed to create user - {result}"
                            )
                            results["failed"] += 1
                            results["user_creation_errors"].append(
                                f"Row {row_num}: {result}"
                            )
                            continue

                        external_user_id = result
                        student_data["external_user_id"] = external_user_id
                        results["users_created"] += 1
                        self._log_step(
                            f"ROW {row_num}",
                            f"✅ MASTER user created: {external_user_id}",
                        )

                    # Map optional fields if they exist
                    for field in optional_fields:
                        if field in df.columns and not pd.isna(row.get(field)):
                            value = row[field]

                            # Handle special field types
                            if field == "admission_date":
                                if isinstance(value, pd.Timestamp):
                                    student_data[field] = value.date()
                                elif isinstance(value, datetime):
                                    student_data[field] = value.date()
                                elif isinstance(value, str):
                                    try:
                                        student_data[field] = datetime.strptime(
                                            value, "%Y-%m-%d"
                                        ).date()
                                    except ValueError:
                                        try:
                                            student_data[field] = datetime.strptime(
                                                value, "%d/%m/%Y"
                                            ).date()
                                        except ValueError:
                                            results["errors"].append(
                                                f"Row {row_num}: Invalid date format for {field}"
                                            )
                                            continue
                                else:
                                    results["errors"].append(
                                        f"Row {row_num}: Invalid date format for {field}"
                                    )
                                    continue
                            elif field == "phone" or field == "emergency_contact_phone":
                                # Convert to string and clean phone numbers
                                phone_str = str(value).strip()
                                # Remove non-numeric characters
                                phone_clean = "".join(filter(str.isdigit, phone_str))
                                if phone_clean:
                                    student_data[field] = phone_clean
                            elif field == "blood_group":
                                blood_group = str(value).strip().upper()
                                valid_blood_groups = [
                                    "A+",
                                    "A-",
                                    "B+",
                                    "B-",
                                    "AB+",
                                    "AB-",
                                    "O+",
                                    "O-",
                                ]
                                if blood_group in valid_blood_groups:
                                    student_data[field] = blood_group
                            else:
                                # Convert all other fields to string
                                student_data[field] = str(value).strip()

                    # Set is_active to True by default
                    student_data["is_active"] = True

                    # Check for duplicate admission number if provided
                    admission_number = student_data.get("admission_number")
                    if admission_number:
                        if Student.objects.filter(
                            admission_number=admission_number, is_active=True
                        ).exists():
                            results["errors"].append(
                                f"Row {row_num}: Admission number '{admission_number}' already exists"
                            )
                            results["failed"] += 1
                            # Rollback master user if created
                            if external_user_id:
                                self._rollback_master_user(external_user_id)
                                results["users_created"] -= 1
                            continue

                    # Check for duplicate student_id if provided
                    student_id = student_data.get("student_id")
                    if student_id:
                        if Student.objects.filter(
                            student_id=student_id, is_active=True
                        ).exists():
                            results["errors"].append(
                                f"Row {row_num}: Student ID '{student_id}' already exists"
                            )
                            results["failed"] += 1
                            # Rollback master user if created
                            if external_user_id:
                                self._rollback_master_user(external_user_id)
                                results["users_created"] -= 1
                            continue

                    # Check for duplicate roll_number if provided
                    roll_number = student_data.get("roll_number")
                    if roll_number:
                        if Student.objects.filter(
                            roll_number=roll_number, is_active=True
                        ).exists():
                            results["errors"].append(
                                f"Row {row_num}: Roll number '{roll_number}' already exists"
                            )
                            results["failed"] += 1
                            # Rollback master user if created
                            if external_user_id:
                                self._rollback_master_user(external_user_id)
                                results["users_created"] -= 1
                            continue

                    # Create student
                    try:
                        serializer = self.get_serializer(data=student_data)
                        serializer.is_valid(raise_exception=True)

                        # Set external_user_id in validated data if created
                        if external_user_id:
                            serializer.validated_data["external_user_id"] = (
                                external_user_id
                            )

                        student = serializer.save()

                        # Create UserProfile in Master DB (same as single create)
                        if external_user_id and hasattr(request, "school"):
                            self._create_user_profile(
                                user_id=external_user_id,
                                school=request.school,
                                role="STUDENT",
                                external_id=student.id,
                            )

                        results["successful"] += 1
                        results["created_students"].append(
                            {
                                "id": student.id,
                                "first_name": student.first_name,
                                "last_name": student.last_name,
                                "student_id": student.student_id,
                                "admission_number": student.admission_number,
                                "external_user_id": (
                                    student.external_user_id
                                    if hasattr(student, "external_user_id")
                                    else None
                                ),
                            }
                        )

                        self._log_step(
                            f"ROW {row_num}",
                            f"✅ Student created: {student.first_name} {student.last_name}",
                        )

                    except Exception as e:
                        error_msg = (
                            f"Row {row_num}: Failed to create student - {str(e)}"
                        )
                        results["errors"].append(error_msg)
                        results["failed"] += 1
                        # Rollback master user if created
                        if external_user_id:
                            self._rollback_master_user(external_user_id)
                            results["users_created"] -= 1
                        self._log_step(
                            f"ROW {row_num}", f"❌ Failed: {str(e)}", "error"
                        )

                except Exception as e:
                    error_msg = f"Row {row_num}: {str(e)}"
                    results["errors"].append(error_msg)
                    results["failed"] += 1
                    self._log_step(f"ROW {row_num}", f"❌ Failed: {str(e)}", "error")

            # Final log
            self._log_step(
                "END",
                f"BULK UPLOAD COMPLETE - Successful: {results['successful']}, Failed: {results['failed']}, Users Created: {results['users_created']}",
            )

            return Response(results, status=status.HTTP_201_CREATED)

        except Exception as e:
            self._log_step("ERROR", f"Bulk upload failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to process Excel file: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    def _parse_boolean(self, value):
        """Parse boolean values from Excel"""
        if pd.isna(value):
            return False
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.lower().strip() in ["true", "yes", "y", "1", "t"]
        if isinstance(value, (int, float)):
            return bool(value)
        return False

    @action(detail=False, methods=["get"], url_path="download-template")
    def download_template(self, request):
        """
        Download Excel template for bulk upload
        """
        # Create template DataFrame with example data
        template_data = {
            "student_id": ["STU001", "STU002"],
            "roll_number": ["1", "2"],
            "admission_number": ["ADM2023001", "ADM2023002"],
            "first_name": ["John", "Jane"],
            "last_name": ["Doe", "Smith"],
            "date_of_birth": ["2005-03-15", "2005-06-20"],
            "gender": ["MALE", "FEMALE"],
            "blood_group": ["A+", "O+"],
            "personal_email": ["john.doe@example.com", "jane.smith@example.com"],
            "phone": ["9876543210", "9876543211"],
            "admission_date": ["2023-04-01", "2023-04-01"],
            "current_class": ["10", "10"],
            "section": ["A", "B"],
            "address": ["123 Main St", "456 Oak Ave"],
            "city": ["New York", "Los Angeles"],
            "state": ["NY", "CA"],
            "pincode": ["10001", "90001"],
            "emergency_contact_name": ["Robert Doe", "Michael Smith"],
            "emergency_contact_phone": ["9876543212", "9876543213"],
            "create_user_account": ["TRUE", "FALSE"],
            "user_email": ["john.doe@example.com", ""],
            "user_password": ["Password@123", ""],
        }

        df = pd.DataFrame(template_data)

        # Create Excel file in memory
        output = BytesIO()
        with pd.ExcelWriter(output, engine="openpyxl") as writer:
            df.to_excel(writer, sheet_name="Template", index=False)

            # Get workbook and worksheet
            workbook = writer.book
            worksheet = writer.sheets["Template"]

            # Add instructions
            instructions = [
                "INSTRUCTIONS:",
                "1. Required fields: first_name, last_name, date_of_birth, gender",
                "2. Date format: YYYY-MM-DD or DD/MM/YYYY",
                "3. Gender must be: MALE, FEMALE, or OTHER",
                "4. Blood group must be: A+, A-, B+, B-, AB+, AB-, O+, O-",
                "5. User creation fields:",
                "   - create_user_account: TRUE/FALSE (default: FALSE)",
                "   - user_email: Email for master DB user (uses personal_email if not provided)",
                "   - user_password: Password for master DB user",
                "6. Do not modify column headers",
                "7. Remove example rows before uploading your data",
                "8. Ensure unique student_id, roll_number, admission_number if provided",
                "9. Emails must be unique in master database",
            ]

            for i, instruction in enumerate(instructions, start=len(df) + 3):
                worksheet.cell(row=i, column=1, value=instruction)

        output.seek(0)

        # Create response
        response = HttpResponse(
            output,
            content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        )
        response["Content-Disposition"] = (
            'attachment; filename="student_bulk_upload_template.xlsx"'
        )

        return response


@action(detail=False, methods=["get"], url_path="list-simple")
def list_simple(self, request):
    """
    Simplified list of students for dropdown selection
    - Initial load: returns only 15 records
    - Search: returns up to 50 matching records
    """
    search = request.query_params.get("search", "").strip()
    
    students = Student.objects.filter(is_active=True)
    
    if search:
        students = students.filter(
            Q(first_name__icontains=search)
            | Q(last_name__icontains=search)
            | Q(student_id__icontains=search)
            | Q(roll_number__icontains=search)
            | Q(admission_number__icontains=search)
            | Q(personal_email__icontains=search)
        ).order_by("first_name", "last_name")[:50]  # Search results: max 50
    else:
        # Initial load: only first 15 records
        students = students.order_by("first_name", "last_name")[:15]
    
    serializer = SimpleStudentSerializer(
        students, many=True, context={"request": request}
    )
    return Response(serializer.data)



    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for students
        """
        stats = {
            "total_students": Student.objects.filter(is_active=True).count(),
            "male": Student.objects.filter(gender="MALE", is_active=True).count(),
            "female": Student.objects.filter(gender="FEMALE", is_active=True).count(),
            "other": Student.objects.filter(gender="OTHER", is_active=True).count(),
            "recent_admissions": Student.objects.filter(is_active=True)
            .order_by("-admission_date")[:10]
            .count(),
        }
        return Response(stats)

    @action(detail=True, methods=["get"])
    def parents(self, request, pk=None):
        """
        Get parents of a specific student
        """
        student = self.get_object()
        parents = StudentParent.objects.filter(student=student, is_active=True)
        serializer = StudentParentSerializer(
            parents, many=True, context={"request": request}
        )
        return Response(serializer.data)

    @action(detail=True, methods=["post"])
    def add_parent(self, request, pk=None):
        """
        Add a parent to a student
        """
        student = self.get_object()
        parent_id = request.data.get("parent_id")
        relationship = request.data.get("relationship", "PARENT")
        is_primary_contact = request.data.get("is_primary_contact", False)

        try:
            parent = Parent.objects.get(id=parent_id, is_active=True)

            # Check if relationship already exists
            if StudentParent.objects.filter(
                student=student, parent=parent, is_active=True
            ).exists():
                return Response(
                    {"error": "This parent is already associated with the student."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Create the relationship
            student_parent = StudentParent.objects.create(
                student=student,
                parent=parent,
                relationship=relationship,
                is_primary_contact=is_primary_contact,
            )

            serializer = StudentParentSerializer(
                student_parent, context={"request": request}
            )
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        except Parent.DoesNotExist:
            return Response(
                {"error": "Parent not found."}, status=status.HTTP_404_NOT_FOUND
            )

    def destroy(self, request, *args, **kwargs):
        """
        Soft delete: Deactivate student and optionally deactivate user in master DB
        """
        instance = self.get_object()

        self._log_step("DELETE", f"Deactivating STUDENT: {instance.id}")

        # Deactivate student
        instance.is_active = False
        instance.save()

        # Optionally deactivate user in master DB
        if instance.external_user_id:
            try:
                user = User.objects.using("default").get(id=instance.external_user_id)
                user.is_active = False
                user.save(using="default")
                self._log_step(
                    "DELETE",
                    f"Deactivated MASTER user: {instance.external_user_id}",
                )
            except User.DoesNotExist:
                self._log_step(
                    "DELETE",
                    f"⚠️ MASTER user not found: {instance.external_user_id}",
                    "warning",
                )

        return Response(status=status.HTTP_204_NO_CONTENT)


class ParentViewSet(BasePeopleViewSet):
    """
    ViewSet for managing parents with multi-tenant support
    """

    queryset = Parent.objects.all()  # Show all parents (active and inactive)
    serializer_class = ParentSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = ["parent_type", "is_primary", "is_active", "gender"]
    search_fields = ["first_name", "last_name", "email", "phone"]
    ordering_fields = ["first_name", "last_name", "created_at"]
    ordering = ["last_name", "first_name"]

    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):
        # Base queryset includes all parents (active and inactive)
        queryset = Parent.objects.all()

        # Filter by is_active if explicitly requested
        is_active = self.request.query_params.get('is_active')
        if is_active is not None:
            if is_active.lower() == 'true':
                queryset = queryset.filter(is_active=True)
            elif is_active.lower() == 'false':
                queryset = queryset.filter(is_active=False)
        # If no filter is applied, show all parents (both active and inactive)

        # ── Filter by gender if explicitly requested ── NEW
        gender = self.request.query_params.get('gender')
        if gender:
            queryset = queryset.filter(gender=gender.upper())

        # Search by name or contact — tokenize so a full "firstname lastname"
        # query matches across the separate first_name/last_name fields.
        search = self.request.query_params.get("search", None)
        if search and search.strip():
            for token in search.split():
                queryset = queryset.filter(
                    Q(first_name__icontains=token)
                    | Q(last_name__icontains=token)
                    | Q(email__icontains=token)
                    | Q(phone__icontains=token)
                )

        return queryset.order_by("last_name", "first_name")

    def _unwrap(self, value, default=None):
        """
        QueryDict.pop() returns a list when the request is multipart/form-data.
        This helper unwraps single-item lists and returns the scalar value,
        or `default` if the value is empty / None.
        """
        if isinstance(value, list):
            value = value[0] if value else default
        return value if value not in (None, '', []) else default

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """
        Create Parent and ALWAYS create a Master DB user account.
        The frontend sends user_email and user_password every time
        (no toggle needed — mirrors StudentViewSet behaviour).
        """
        self._log_step("START", "PARENT CREATION PROCESS")

        try:
            data = request.data.copy()

            # ── Unwrap multipart/form-data lists ───────────────────────────────
            # QueryDict.pop() returns lists for multipart data; unwrap them first.
            create_user_account_raw = self._unwrap(data.pop("create_user_account", None), "true")
            if isinstance(create_user_account_raw, str):
                create_user_account = create_user_account_raw.lower() == "true"
            else:
                create_user_account = bool(create_user_account_raw)

            user_email = self._unwrap(data.pop("user_email", None)) or self._unwrap(data.get("email"))
            user_password = self._unwrap(data.pop("user_password", None))

            self._log_step("USER_CREATION", f"create_user_account: {create_user_account}")
            self._log_step("USER_CREATION", f"user_email: {user_email}")
            self._log_step("USER_CREATION", f"user_password: {'[PRESENT]' if user_password else '[MISSING]'}")

            # ── Validate required account fields ───────────────────────────────
            if not user_email:
                return Response(
                    {"error": "An email address is required to create the parent user account."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if not user_password:
                return Response(
                    {"error": "A password is required to create the parent user account."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # ── Check for duplicate in master DB ──────────────────────────────
            if User.objects.using("default").filter(email=user_email).exists():
                return Response(
                    {"error": f"User with email {user_email} already exists in master database"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # ── Step 1: Create User in Master DB ──────────────────────────────
            user_data = {
                "email": user_email,
                "password": user_password,
                "first_name": self._unwrap(data.get("first_name"), ""),
                "last_name": self._unwrap(data.get("last_name"), ""),
                "username": user_email,
            }

            success, result = self._create_master_user(user_data, "PARENT")
            if not success:
                return Response({"error": result}, status=status.HTTP_400_BAD_REQUEST)

            external_user_id = result
            self._log_step("MASTER_CREATE", f"✅ Created master user: {external_user_id}")

            # ── Step 2: Validate Parent data ──────────────────────────────────
            self._log_step("1", "Validating Parent data")

            # Strip user-account fields before passing to serializer
            for field in ("user_email", "create_user_account", "user_password", "confirm_password"):
                data.pop(field, None)

            serializer = self.get_serializer(data=data)
            serializer.is_valid(raise_exception=True)

            # ── Step 3: Create Parent in School DB ────────────────────────────
            self._log_step("2", "Creating Parent in SCHOOL DB")

            serializer.validated_data["external_user_id"] = external_user_id

            parent = serializer.save()
            self._log_step("2", f"✅ PARENT created: {parent.id}")

            # ── Step 4: Create UserProfile in Master DB ───────────────────────
            if hasattr(request, "school"):
                self._create_user_profile(
                    user_id=external_user_id,
                    school=request.school,
                    role="PARENT",
                    external_id=parent.id,
                )

            self._log_step("END", f"PARENT CREATION COMPLETE - ID: {parent.id}")

            headers = self.get_success_headers(serializer.data)
            return Response(
                serializer.data, status=status.HTTP_201_CREATED, headers=headers
            )

        except Exception as e:
            self._log_step("ERROR", f"Parent creation failed: {str(e)}", "error")
            if "external_user_id" in locals() and external_user_id:
                self._rollback_master_user(external_user_id)

            return Response(
                {"error": f"Failed to create parent: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @transaction.atomic
    def update(self, request, *args, **kwargs):
        """
        Update Parent and optionally linked User.
        Supports `remove_profile_image=true` to clear an existing profile picture.
        """
        self._log_step("START", "PARENT UPDATE PROCESS")

        partial = kwargs.pop("partial", False)
        instance = self.get_object()

        try:
            data = request.data.copy()

            # ── Handle profile image removal ───────────────────────────────────
            remove_profile_image_raw = self._unwrap(data.pop("remove_profile_image", None), "false")
            if isinstance(remove_profile_image_raw, str):
                remove_profile_image = remove_profile_image_raw.lower() == "true"
            else:
                remove_profile_image = bool(remove_profile_image_raw)

            if remove_profile_image and hasattr(instance, 'profile_image') and instance.profile_image:
                self._log_step("IMG_REMOVE", f"Removing profile image for parent: {instance.id}")
                instance.profile_image.delete(save=False)
                instance.profile_image = None

            # ── Extract optional password change ───────────────────────────────
            data.pop("update_user", None)
            user_password = self._unwrap(data.pop("user_password", None))
            data.pop("user_email", None)
            for field in ("create_user_account", "confirm_password"):
                data.pop(field, None)

            # ── Step 1: Update Parent in School DB ────────────────────────────
            self._log_step("1", f"Updating PARENT in SCHOOL DB: {instance.id}")

            serializer = self.get_serializer(instance, data=data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)

            # ── Auto-sync identity fields to master DB ─────────────────────────
            if instance.external_user_id:
                updated = serializer.instance
                self._sync_to_master_db(
                    external_user_id=instance.external_user_id,
                    first_name=updated.first_name,
                    last_name=updated.last_name,
                    email=updated.email,
                    password=user_password or None,
                )

            self._log_step("END", f"PARENT UPDATE COMPLETE - ID: {instance.id}")

            return Response(serializer.data)

        except Exception as e:
            self._log_step("ERROR", f"Parent update failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to update parent: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=False, methods=["get"], url_path="list-simple")
    def list_simple(self, request):
        """
        Simplified list of parents for dropdown selection.
        For dropdowns, we still only show active parents.
        """
        parents = Parent.objects.filter(is_active=True).order_by("first_name", "last_name")
        serializer = SimpleParentSerializer(parents, many=True, context={"request": request})
        return Response(serializer.data)

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """
        Get statistics for parents
        """
        stats = {
            "total_parents": Parent.objects.filter(is_active=True).count(),
            "fathers": Parent.objects.filter(parent_type="FATHER", is_active=True).count(),
            "mothers": Parent.objects.filter(parent_type="MOTHER", is_active=True).count(),
            "guardians": Parent.objects.filter(parent_type="GUARDIAN", is_active=True).count(),
            "other": Parent.objects.filter(parent_type="OTHER", is_active=True).count(),
            "primary_contacts": Parent.objects.filter(is_primary=True, is_active=True).count(),
        }
        return Response(stats)

    def destroy(self, request, *args, **kwargs):
        """
        Soft delete: Deactivate parent and optionally deactivate user in master DB
        """
        instance = self.get_object()

        self._log_step("DELETE", f"Deactivating PARENT: {instance.id}")

        # Deactivate parent
        instance.is_active = False
        instance.save()

        # Optionally deactivate user in master DB
        if instance.external_user_id:
            try:
                user = User.objects.using("default").get(id=instance.external_user_id)
                user.is_active = False
                user.save(using="default")
                self._log_step("DELETE", f"Deactivated MASTER user: {instance.external_user_id}")
            except User.DoesNotExist:
                self._log_step(
                    "DELETE",
                    f"⚠️ MASTER user not found: {instance.external_user_id}",
                    "warning",
                )

        return Response(status=status.HTTP_204_NO_CONTENT)
    
class StudentParentViewSet(BasePeopleViewSet):
    """
    ViewSet for managing Student-Parent relationships
    """

    queryset = StudentParent.objects.all()  # Show all (active and inactive) — matches Students/Parents/Teachers
    serializer_class = StudentParentSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = [
        "relationship",
        "is_primary_contact",
        "can_pickup",
        "has_medical_consent",
        "is_active",
    ]
    search_fields = [
        "student__first_name",
        "student__last_name",
        "student__student_id",
        "parent__first_name",
        "parent__last_name",
        "parent__email",
    ]
    ordering_fields = ["created_at", "updated_at"]
    ordering = ["-created_at"]

    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 = StudentParent.objects.all().select_related("student", "parent")

        # ── is_active filter — show ALL by default, filter only when explicitly requested ──
        is_active = self.request.query_params.get("is_active")
        if is_active is not None:
            if is_active.lower() == "true":
                queryset = queryset.filter(is_active=True)
            elif is_active.lower() == "false":
                queryset = queryset.filter(is_active=False)
        # If no filter → show both active and inactive (consistent with other ViewSets)

        # ── is_primary_contact filter ──────────────────────────────────────────
        is_primary = self.request.query_params.get("is_primary_contact")
        if is_primary is not None:
            if is_primary.lower() == "true":
                queryset = queryset.filter(is_primary_contact=True)
            elif is_primary.lower() == "false":
                queryset = queryset.filter(is_primary_contact=False)

        # ── student_id filter ──────────────────────────────────────────────────
        student_id = self.request.query_params.get("student_id")
        if student_id:
            queryset = queryset.filter(student_id=student_id)

        # ── parent_id filter ───────────────────────────────────────────────────
        parent_id = self.request.query_params.get("parent_id")
        if parent_id:
            queryset = queryset.filter(parent_id=parent_id)

        # ── search ─────────────────────────────────────────────────────────────
        # Tokenize so a full "firstname lastname" query matches across the
        # separate first_name/last_name fields (on either side of the
        # relationship).
        search = self.request.query_params.get("search")
        if search and search.strip():
            for token in search.split():
                queryset = queryset.filter(
                    Q(student__first_name__icontains=token)
                    | Q(student__last_name__icontains=token)
                    | Q(student__student_id__icontains=token)
                    | Q(parent__first_name__icontains=token)
                    | Q(parent__last_name__icontains=token)
                    | Q(parent__email__icontains=token)
                )

        return queryset.order_by("-created_at")

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """
        Create Student-Parent relationship
        """
        self._log_step("START", "STUDENT-PARENT RELATIONSHIP CREATION PROCESS")

        try:
            data = request.data.copy()

            student_id = data.get("student")
            parent_id = data.get("parent")

            if not student_id:
                return Response({"error": "Student ID is required"}, status=status.HTTP_400_BAD_REQUEST)

            if not parent_id:
                return Response({"error": "Parent ID is required"}, status=status.HTTP_400_BAD_REQUEST)

            # Check if relationship already exists (active)
            if StudentParent.objects.filter(student_id=student_id, parent_id=parent_id, is_active=True).exists():
                return Response(
                    {"error": "This parent is already associated with the student"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if student exists and is active
            try:
                student = Student.objects.get(id=student_id, is_active=True)
            except Student.DoesNotExist:
                return Response({"error": "Student not found or inactive"}, status=status.HTTP_404_NOT_FOUND)

            # Check if parent exists and is active
            try:
                parent = Parent.objects.get(id=parent_id, is_active=True)
            except Parent.DoesNotExist:
                return Response({"error": "Parent not found or inactive"}, status=status.HTTP_404_NOT_FOUND)

            # If setting as primary contact, remove primary from other relationships
            is_primary_contact = data.get("is_primary_contact", False)
            if is_primary_contact:
                StudentParent.objects.filter(
                    student=student, is_primary_contact=True, is_active=True
                ).update(is_primary_contact=False)

            serializer = self.get_serializer(data=data)
            serializer.is_valid(raise_exception=True)

            student_parent = serializer.save()
            self._log_step("CREATE", f"✅ STUDENT-PARENT relationship created: {student_parent.id}")
            self._log_step("END", f"RELATIONSHIP CREATION COMPLETE - ID: {student_parent.id}")

            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

        except Exception as e:
            self._log_step("ERROR", f"Student-Parent creation failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to create student-parent relationship: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @transaction.atomic
    def update(self, request, *args, **kwargs):
        """
        Update Student-Parent relationship
        """
        self._log_step("START", "STUDENT-PARENT RELATIONSHIP UPDATE PROCESS")

        partial = kwargs.pop("partial", False)
        instance = self.get_object()

        try:
            data = request.data.copy()

            # If setting is_primary_contact to True, demote existing primary
            is_primary_contact = data.get("is_primary_contact")
            if is_primary_contact in (True, "true", "True"):
                StudentParent.objects.filter(
                    student=instance.student, is_primary_contact=True, is_active=True
                ).exclude(id=instance.id).update(is_primary_contact=False)

            serializer = self.get_serializer(instance, data=data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)

            self._log_step("END", f"RELATIONSHIP UPDATE COMPLETE - ID: {instance.id}")
            return Response(serializer.data)

        except Exception as e:
            self._log_step("ERROR", f"Student-Parent update failed: {str(e)}", "error")
            return Response(
                {"error": f"Failed to update student-parent relationship: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    def destroy(self, request, *args, **kwargs):
        """
        Hard delete student-parent relationship
        """
        instance = self.get_object()
        self._log_step("DELETE", f"Deleting STUDENT-PARENT relationship: {instance.id}")

        # If this was the primary contact, auto-promote another
        if instance.is_primary_contact:
            other = StudentParent.objects.filter(
                student=instance.student, is_active=True
            ).exclude(id=instance.id).first()
            if other:
                other.is_primary_contact = True
                other.save()
                self._log_step("DELETE", f"Auto-promoted new primary contact: {other.id}")

        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(detail=False, methods=["get"], url_path="by-student/(?P<student_id>[^/.]+)")
    def by_student(self, request, student_id=None):
        try:
            student = Student.objects.get(id=student_id, is_active=True)
            relationships = StudentParent.objects.filter(student=student, is_active=True).select_related("parent")
            serializer = self.get_serializer(relationships, many=True)
            return Response(serializer.data)
        except Student.DoesNotExist:
            return Response({"error": "Student not found"}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=False, methods=["get"], url_path="by-parent/(?P<parent_id>[^/.]+)")
    def by_parent(self, request, parent_id=None):
        try:
            parent = Parent.objects.get(id=parent_id, is_active=True)
            relationships = StudentParent.objects.filter(parent=parent, is_active=True).select_related("student")
            serializer = self.get_serializer(relationships, many=True)
            return Response(serializer.data)
        except Parent.DoesNotExist:
            return Response({"error": "Parent not found"}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="set-primary")
    def set_as_primary(self, request, pk=None):
        instance = self.get_object()
        try:
            StudentParent.objects.filter(
                student=instance.student, is_primary_contact=True, is_active=True
            ).exclude(id=instance.id).update(is_primary_contact=False)
            instance.is_primary_contact = True
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
        except Exception as e:
            return Response({"error": f"Failed to set as primary: {str(e)}"}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], url_path="update-permissions")
    def update_permissions(self, request, pk=None):
        instance = self.get_object()
        try:
            data = request.data
            for field in ("can_pickup", "has_medical_consent", "has_academic_access", "receive_notifications", "notification_preferences"):
                if field in data:
                    setattr(instance, field, data[field])
            instance.save()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
        except Exception as e:
            return Response({"error": f"Failed to update permissions: {str(e)}"}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"], url_path="primary-contacts")
    def primary_contacts(self, request):
        primary_contacts = StudentParent.objects.filter(
            is_primary_contact=True, is_active=True
        ).select_related("student", "parent")
        serializer = self.get_serializer(primary_contacts, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["get"], url_path="statistics")
    def statistics(self, request):
        stats = {
            "total_relationships": StudentParent.objects.filter(is_active=True).count(),
            "primary_contacts": StudentParent.objects.filter(is_primary_contact=True, is_active=True).count(),
            "can_pickup": StudentParent.objects.filter(can_pickup=True, is_active=True).count(),
            "has_medical_consent": StudentParent.objects.filter(has_medical_consent=True, is_active=True).count(),
            "fathers": StudentParent.objects.filter(relationship="FATHER", is_active=True).count(),
            "mothers": StudentParent.objects.filter(relationship="MOTHER", is_active=True).count(),
            "guardians": StudentParent.objects.filter(relationship="GUARDIAN", is_active=True).count(),
        }
        return Response(stats)

    # ── NEW: searchable student list for the dialog dropdown ──────────────────
    # ── NEW: searchable student list for the dialog dropdown ──────────────────
    @action(detail=False, methods=["get"], url_path="students-list-simple")
    def students_list_simple(self, request):
        """
        Lightweight list of active students for the relationship dialog dropdown.
        Supports ?search= to filter by first_name, last_name, student_id, or
        admission_number.
        - Initial load: returns only 15 records
        - With search: returns up to 50 matching records for better usability
        """
        queryset = Student.objects.filter(is_active=True)

        search = request.query_params.get("search", "").strip()
        if search:
            # Tokenize so a full "firstname lastname" query matches across
            # the separate first_name/last_name fields.
            for token in search.split():
                queryset = queryset.filter(
                    Q(first_name__icontains=token)
                    | Q(last_name__icontains=token)
                    | Q(student_id__icontains=token)
                    | Q(admission_number__icontains=token)
                )
            queryset = queryset.order_by("first_name", "last_name")[:50]  # Search: max 50
        else:
            queryset = queryset.order_by("first_name", "last_name")[:15]  # Initial: 15 records

        serializer = SimpleStudentSerializer(
            queryset, many=True, context={"request": request}
        )
        return Response(serializer.data)
    @action(detail=False, methods=["get"], url_path="parents-list-simple")
    def parents_list_simple(self, request):
        """
        Lightweight list of active parents for the relationship dialog dropdown.
        Supports ?search= to filter by first_name, last_name, email, or phone.
        Results are capped at 100 rows for performance.

        Example:
            GET /people/student-parents/parents-list-simple/?search=kumar
        """
        queryset = Parent.objects.filter(is_active=True)

        search = request.query_params.get("search", "").strip()
        if search:
            # Tokenize so a full "firstname lastname" query matches across
            # the separate first_name/last_name fields.
            for token in search.split():
                queryset = queryset.filter(
                    Q(first_name__icontains=token)
                    | Q(last_name__icontains=token)
                    | Q(email__icontains=token)
                    | Q(phone__icontains=token)
                )

        queryset = queryset.order_by("first_name", "last_name")[:100]

        serializer = SimpleParentSerializer(
            queryset, many=True, context={"request": request}
        )
        return Response(serializer.data)


# ─── Teacher Activity Views ───────────────────────────────────────────────────

class TeacherActivityListView(APIView):
    """
    GET /people/teachers/activities/?days=30&page=1&page_size=20
    Returns paginated active teachers sorted by recent activity count.
    Activity counts use two sources:
      - explicit FK (taken_by / posted_by / checked_by) — most accurate
      - class-based fallback via SubjectTeacher assignments when FK is null
    Last active falls back to User.last_login from the master DB.
    """
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        from django.utils import timezone as tz
        from datetime import timedelta
        from django.db.models import Count, Max
        from attendance.models import AttendanceSession
        from tasks.models import ClassTask, StudentTaskSubmission
        from academics.models import SubjectTeacher

        days = int(request.query_params.get('days', 30))
        page = max(int(request.query_params.get('page', 1)), 1)
        page_size = min(int(request.query_params.get('page_size', 20)), 100)
        since_date = (tz.now() - timedelta(days=days)).date()

        teachers = list(Teacher.objects.filter(is_active=True).order_by('last_name', 'first_name'))
        teacher_ids = [t.id for t in teachers]

        # ── Batch 1: attendance sessions where taken_by is explicitly set ──
        att_explicit = {
            r['taken_by_id']: r
            for r in AttendanceSession.objects
            .filter(taken_by_id__in=teacher_ids, date__gte=since_date)
            .values('taken_by_id')
            .annotate(cnt=Count('id'), last=Max('updated_at'))
        }

        # ── Batch 2: class assignments per teacher (SubjectTeacher) ─────────
        st_qs = list(
            SubjectTeacher.objects
            .filter(teacher_id__in=teacher_ids, is_active=True)
            .values('teacher_id', 'academic_class_id', 'subject__name')
        )
        # class_ids per teacher
        teacher_class_map = {}
        subj_map = {}
        for row in st_qs:
            tid = row['teacher_id']
            teacher_class_map.setdefault(tid, set()).add(row['academic_class_id'])
            name = row['subject__name']
            if name and name not in subj_map.get(tid, []):
                subj_map.setdefault(tid, []).append(name)

        # For teachers without explicit taken_by, count sessions in their classes
        all_class_ids = set()
        for cids in teacher_class_map.values():
            all_class_ids.update(cids)

        class_att_qs = {}
        if all_class_ids:
            for row in (
                AttendanceSession.objects
                .filter(academic_class_id__in=all_class_ids, date__gte=since_date, taken_by__isnull=True)
                .values('academic_class_id')
                .annotate(cnt=Count('id'), last=Max('updated_at'))
            ):
                class_att_qs[row['academic_class_id']] = row

        # ── Batch 3: tasks posted ────────────────────────────────────────────
        task_map = {
            r['posted_by_id']: r
            for r in ClassTask.objects
            .filter(posted_by_id__in=teacher_ids, created_at__date__gte=since_date, is_active=True)
            .values('posted_by_id')
            .annotate(cnt=Count('id'), last=Max('created_at'))
        }

        # ── Batch 4: submissions checked ─────────────────────────────────────
        checked_map = {
            r['checked_by_id']: r
            for r in StudentTaskSubmission.objects
            .filter(checked_by_id__in=teacher_ids, checked_at__date__gte=since_date, checked_at__isnull=False)
            .values('checked_by_id')
            .annotate(cnt=Count('id'), last=Max('checked_at'))
        }

        # ── Batch 5: last_login from master DB via external_user_id ─────────
        ext_id_map = {}  # external_user_id (str) → teacher.id
        for t in teachers:
            if t.external_user_id:
                ext_id_map[t.external_user_id] = t.id

        login_by_teacher = {}
        if ext_id_map:
            try:
                User = get_user_model()
                # external_user_id stores the master DB user PK as a string
                int_ext_ids = []
                for eid in ext_id_map:
                    try:
                        int_ext_ids.append(int(eid))
                    except (ValueError, TypeError):
                        pass
                if int_ext_ids:
                    for u in User.objects.using('default').filter(id__in=int_ext_ids).values('id', 'last_login'):
                        tid = ext_id_map.get(str(u['id']))
                        if tid:
                            login_by_teacher[tid] = u['last_login']
            except Exception:
                pass  # master DB unavailable — degrade gracefully

        # ── Build result rows ────────────────────────────────────────────────
        results = []
        for teacher in teachers:
            tid = teacher.id

            # Attendance: explicit taken_by first, then class-based fallback
            att_explicit_r = att_explicit.get(tid, {})
            att_explicit_cnt = att_explicit_r.get('cnt', 0)
            att_explicit_last = att_explicit_r.get('last')

            att_class_cnt = 0
            att_class_last = None
            for cid in teacher_class_map.get(tid, []):
                row = class_att_qs.get(cid, {})
                att_class_cnt += row.get('cnt', 0)
                cdate = row.get('last')
                if cdate and (att_class_last is None or cdate > att_class_last):
                    att_class_last = cdate

            att_cnt  = att_explicit_cnt + att_class_cnt
            att_last = max((d for d in [att_explicit_last, att_class_last] if d), default=None)

            tasks    = task_map.get(tid, {})
            task_cnt = tasks.get('cnt', 0)
            task_last = tasks.get('last')

            checked     = checked_map.get(tid, {})
            checked_cnt = checked.get('cnt', 0)
            checked_last = checked.get('last')

            total = att_cnt + task_cnt + checked_cnt

            # Last active: best of activity timestamps or last_login
            candidate_dates = [att_last, task_last, checked_last, login_by_teacher.get(tid)]
            last_active = max((d for d in candidate_dates if d), default=None)
            # Absolute fallback: teacher record updated_at
            if last_active is None:
                last_active = teacher.updated_at

            if total >= 10:
                activity_status = 'ACTIVE'
            elif total > 0:
                activity_status = 'LOW'
            else:
                activity_status = 'INACTIVE'

            profile_url = request.build_absolute_uri(teacher.profile_image.url) if teacher.profile_image else None
            full_name = ' '.join(filter(None, [teacher.first_name, teacher.last_name])) or f"Teacher #{tid}"

            results.append({
                'id': tid,
                'name': full_name,
                'email': teacher.email or '',
                'phone': teacher.phone or '',
                'employee_id': teacher.employee_id or '',
                'employment_type': teacher.employment_type or '',
                'profile_image': profile_url,
                'subjects': subj_map.get(tid, []),
                'last_active': last_active,
                'attendance_sessions': att_cnt,
                'tasks_posted': task_cnt,
                'submissions_checked': checked_cnt,
                'total_activities': total,
                'activity_status': activity_status,
            })

        results.sort(key=lambda x: x['total_activities'], reverse=True)

        total_count = len(results)
        offset = (page - 1) * page_size
        paginated = results[offset: offset + page_size]

        return Response({
            'count': total_count,
            'page': page,
            'page_size': page_size,
            'total_pages': (total_count + page_size - 1) // page_size,
            'days': days,
            'results': paginated,
        })


class TeacherActivityDetailView(APIView):
    """
    GET /people/teachers/<teacher_id>/activity-detail/?days=30
    Returns timeline of recent activities for a single teacher.
    """
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, teacher_id):
        from django.utils import timezone as tz
        from datetime import timedelta
        from django.db.models import Count
        from django.shortcuts import get_object_or_404
        from attendance.models import AttendanceSession
        from tasks.models import ClassTask, StudentTaskSubmission
        from academics.models import SubjectTeacher

        teacher = get_object_or_404(Teacher, id=teacher_id, is_active=True)
        days = int(request.query_params.get('days', 30))
        since_date = (tz.now() - timedelta(days=days)).date()

        activities = []

        # ─── Attendance sessions taken ────────────────────────────────────
        sessions = (
            AttendanceSession.objects
            .filter(taken_by=teacher, date__gte=since_date)
            .select_related(
                'academic_class__standard',
                'academic_class__section',
                'subject',
            )
            .order_by('-date', '-updated_at')[:60]
        )
        for s in sessions:
            std = getattr(getattr(s.academic_class, 'standard', None), 'name', '')
            sec = getattr(getattr(s.academic_class, 'section', None), 'name', '')
            class_name = f"{std} {sec}".strip() if (std or sec) else f"Class #{s.academic_class_id}"
            activities.append({
                'type': 'attendance',
                'title': f"Took attendance — {class_name}",
                'detail': f"{s.get_session_type_display()} · {s.total_students} students · {s.present_count} present, {s.absent_count} absent",
                'date': str(s.date),
                'timestamp': s.updated_at.isoformat(),
                'status': s.status,
                'meta': {
                    'class_name': class_name,
                    'session_type': s.session_type,
                    'present': s.present_count,
                    'absent': s.absent_count,
                    'late': s.late_count,
                    'leave': s.leave_count,
                    'total': s.total_students,
                    'session_status': s.status,
                }
            })

        # ─── Tasks posted ─────────────────────────────────────────────────
        tasks = (
            ClassTask.objects
            .filter(posted_by=teacher, created_at__date__gte=since_date, is_active=True)
            .select_related(
                'academic_class__standard',
                'academic_class__section',
                'subject',
                'task_type',
            )
            .order_by('-created_at')[:60]
        )
        for t in tasks:
            std = getattr(getattr(t.academic_class, 'standard', None), 'name', '')
            sec = getattr(getattr(t.academic_class, 'section', None), 'name', '')
            class_name = f"{std} {sec}".strip() if (std or sec) else f"Class #{t.academic_class_id}"
            subj_name = t.subject.name if t.subject else '—'
            activities.append({
                'type': 'task',
                'title': f"Posted task — {t.title}",
                'detail': f"{class_name} · {subj_name}{' · Due ' + str(t.due_date) if t.due_date else ''}",
                'date': str(t.created_at.date()),
                'timestamp': t.created_at.isoformat(),
                'status': 'PUBLISHED' if t.is_published else 'DRAFT',
                'meta': {
                    'class_name': class_name,
                    'subject': subj_name,
                    'task_type': t.task_type.name if t.task_type else '',
                    'due_date': str(t.due_date) if t.due_date else None,
                    'is_published': t.is_published,
                }
            })

        # ─── Submissions checked ──────────────────────────────────────────
        checked = (
            StudentTaskSubmission.objects
            .filter(checked_by=teacher, checked_at__date__gte=since_date, checked_at__isnull=False)
            .select_related(
                'student_task__class_task__subject',
                'student_task__class_task__academic_class__standard',
                'student_task__class_task__academic_class__section',
                'student_task__student',
            )
            .order_by('-checked_at')[:60]
        )
        for sub in checked:
            ct = sub.student_task.class_task if sub.student_task else None
            task_title = ct.title if ct else 'Unknown Task'
            subj_name = ct.subject.name if ct and ct.subject else '—'
            student = sub.student_task.student if sub.student_task else None
            student_name = (
                ' '.join(filter(None, [student.first_name, student.last_name]))
                if student else '—'
            )
            activities.append({
                'type': 'checked',
                'title': f"Checked submission — {task_title}",
                'detail': f"{student_name} · {subj_name}",
                'date': str(sub.checked_at.date()),
                'timestamp': sub.checked_at.isoformat(),
                'status': sub.status,
                'meta': {
                    'task_title': task_title,
                    'subject': subj_name,
                    'student': student_name,
                    'marks_obtained': str(sub.marks_obtained) if sub.marks_obtained else None,
                }
            })

        activities.sort(key=lambda x: x['timestamp'], reverse=True)

        att_total = AttendanceSession.objects.filter(taken_by=teacher, date__gte=since_date).count()
        task_total = ClassTask.objects.filter(posted_by=teacher, created_at__date__gte=since_date, is_active=True).count()
        checked_total = StudentTaskSubmission.objects.filter(checked_by=teacher, checked_at__date__gte=since_date, checked_at__isnull=False).count()

        subjects = list(
            SubjectTeacher.objects
            .filter(teacher=teacher, is_active=True)
            .select_related('subject')
            .values_list('subject__name', flat=True)
            .distinct()
        )

        profile_url = request.build_absolute_uri(teacher.profile_image.url) if teacher.profile_image else None
        full_name = (
            ' '.join(filter(None, [teacher.first_name, teacher.last_name]))
            or f"Teacher #{teacher.id}"
        )

        return Response({
            'teacher': {
                'id': teacher.id,
                'name': full_name,
                'email': teacher.email or '',
                'phone': teacher.phone or '',
                'employee_id': teacher.employee_id or '',
                'employment_type': teacher.employment_type or '',
                'profile_image': profile_url,
                'qualification': teacher.qualification or '',
                'date_of_joining': str(teacher.date_of_joining) if teacher.date_of_joining else None,
                'subjects': subjects,
            },
            'summary': {
                'attendance_sessions': att_total,
                'tasks_posted': task_total,
                'submissions_checked': checked_total,
                'total': att_total + task_total + checked_total,
                'days': days,
            },
            'activities': activities,
        })