# api/views.py (Updated with SystemSetting integration)
import logging
import re
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes, parser_classes
from rest_framework.permissions import IsAuthenticated, AllowAny, BasePermission
from rest_framework_simplejwt.tokens import RefreshToken, TokenError
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import status
from django.contrib.auth.models import update_last_login
from django.shortcuts import get_object_or_404
from django.db.models import Avg, Count, Sum
from .models import *
from .serializers import *
from datetime import datetime, date
import csv
from io import StringIO
from django.db import transaction
from django.core.cache import cache
logger = logging.getLogger(__name__)
# Custom permission class
class IsSchoolAdmin(BasePermission):
    """
    Allows access if user is SUPER_ADMIN or ADMIN.
    """
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.role.upper() in ['SUPER_ADMIN', 'ADMIN']
def get_system_settings():
    cached_settings = cache.get('system_settings')
    if cached_settings is None:
        settings = SystemSetting.objects.all()
        cached_settings = {s.key: s.value for s in settings}
        cache.set('system_settings', cached_settings, timeout=3600) # Cache for 1 hour
    return cached_settings
def update_system_setting(key, value):
    setting, created = SystemSetting.objects.get_or_create(key=key)
    setting.value = value
    setting.save()
    cache.delete('system_settings') # Invalidate cache on update
class LoginView(APIView):
    permission_classes = [AllowAny]
    def post(self, request):
        identifier = request.data.get('email')
        password = request.data.get('password')
        login_type = request.data.get('login_type')  # new
        if not identifier or not password:
            return Response({"error": "Email and password are required"}, status=400)
        # Manual user lookup: try username first, then email
        try:
            user = CustomUser.objects.get(username=identifier)
        except CustomUser.DoesNotExist:
            try:
                user = CustomUser.objects.get(email=identifier)
            except CustomUser.DoesNotExist:
                return Response({"error": "Invalid credentials"}, status=401)
        if not user.check_password(password):
            return Response({"error": "Invalid credentials"}, status=401)
        if not user.is_active:
            return Response({"error": "User account is disabled"}, status=401)
        # ==== NEW: Role/tab validation ====
        user_role = user.role.upper()

        if login_type:
            if login_type == "student":
                if user_role != "LEARNER":
                    return Response(
                        {"error": "Please use the Student tab for admission number login."},
                        status=401
                    )
            elif login_type == "parent":
                if user_role != "PARENT":
                    return Response(
                        {"error": "This is not a parent account. Please use the Staff tab."},
                        status=401
                    )
            elif login_type == "staff":
                if user_role not in ["TEACHER", "ADMIN", "SUPER_ADMIN"]:
                    return Response(
                        {"error": "This is not a staff account. Please use the Parent tab."},
                        status=401
                    )
        # ==== End of new validation ====
        update_last_login(None, user)
        refresh = RefreshToken.for_user(user)
        user_data = UserSerializer(user).data
        user_data['mustChangePassword'] = user.must_change_password
        return Response({
            "accessToken": str(refresh.access_token),
            "refreshToken": str(refresh),
            "user": user_data
        }, status=200)
class RefreshTokenView(APIView):
    permission_classes = [AllowAny]
    def post(self, request):
        refresh_token = request.data.get('refreshToken')
        if not refresh_token:
            return Response({"error": "Refresh token required"}, status=400)
        try:
            refresh = RefreshToken(refresh_token)
            return Response({"accessToken": str(refresh.access_token)}, status=200)
        except TokenError as e:
            return Response({"error": str(e)}, status=401)
class LogoutView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self, request):
        return Response({"message": "Logout successful"}, status=200)
class ChangePasswordView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self, request):
        user = request.user
        old = request.data.get('old_password')
        new = request.data.get('new_password')
        if not old or not new:
            return Response({"error": "old_password and new_password required"}, status=400)
        if not user.check_password(old):
            return Response({"error": "Old password incorrect"}, status=400)
        user.set_password(new)
        user.must_change_password = False
        user.save(update_fields=['password', 'must_change_password'])
        return Response({"message": "Password changed successfully"}, status=200)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_grades(request):
    grades = Grade.objects.all().order_by('code')
    data = [{"id": g.code, "name": g.name} for g in grades]
    return Response(data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_users(request):
    """
    List all users - accessible to authenticated users with appropriate permissions
    """
    # Check if user is admin or has permission
    if not request.user.is_authenticated:
        return Response({"error": "Authentication required"}, status=401)
    # Allow admins and teachers (teachers might need to see students)
    allowed_roles = ['super_admin', 'admin', 'teacher']
    if request.user.role.lower() not in allowed_roles:
        return Response({"error": "Permission denied"}, status=403)
    role = request.query_params.get('role') # Get ?role= from query
    users = CustomUser.objects.all()
    # Filter by role if specified
    if role:
        users = users.filter(role__iexact=role)
    # Teachers can only see students in their classes
    if request.user.role.lower() == 'teacher':
        # Get classes taught by this teacher
        allocations = TeacherAllocation.objects.filter(teacher=request.user)
        class_streams = [alloc.class_stream for alloc in allocations]
   
        # Get students in those classes
        users = users.filter(
            role='learner',
            class_stream__in=class_streams
        )
    serializer = UserSerializer(users, many=True)
    return Response(serializer.data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_teacher(request):
    data = request.data
    name = data.get('name', '').strip()
    email = data.get('email', '').strip().lower()
    phone = data.get('phone', '').strip()
    if not name or not email:
        return Response({"error": "Name and email required for teacher"}, status=400)
    if CustomUser.objects.filter(email=email).exists():
        return Response({"error": "Email already exists"}, status=400)
    first, *last = name.split()
    user = CustomUser.objects.create(
        username=email,
        email=email,
        first_name=first,
        last_name=' '.join(last),
        phone=phone,
        role='teacher',
        must_change_password=True
    )
    default_password = SystemSetting.objects.get(key='default_password').value
    user.set_password(default_password)
    user.save()
    return Response({
        "message": "Teacher created",
        "temporary_password": default_password
    }, status=201)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_learner(request):
    data = request.data
    logger.info(f"Received data for learner creation: {data}") # Log incoming data to verify parentData
    name = data.get('name', '').strip()
    email = data.get('email', '').strip().lower() or None
    admission = data.get('admissionNumber', '').strip()
    grade_id = data.get('gradeLevelId', '') # Now saved
    if not name:
        return Response({"error": "Name required for student"}, status=400)
    if not admission:
        return Response({"error": "Admission number required for student"}, status=400)
    username = re.sub(r'\s+', '-', admission).upper()  # ← normalised
    if CustomUser.objects.filter(username=username).exists():
        return Response({"error": "Admission number already exists"}, status=400)
    first, *last = name.split()
    if not email:
        email = f"{first.lower()}.{admission.lower()}@bethel.school"
    if CustomUser.objects.filter(email=email).exists():
        return Response({"error": "Email conflict"}, status=400)
    try:
        user = CustomUser(
            username=username,
            email=email,
            first_name=first,
            last_name=' '.join(last),
            role='learner',
            must_change_password=True,
            grade_level=grade_id # Save grade
        )
        default_password = SystemSetting.objects.get(key='default_password').value
        user.set_password(default_password)
        user.full_clean() # Validate before save
        user.save()
        logger.info(f"Created learner: ID={user.id}, email={user.email}, username={user.username}, grade={user.grade_level}")
    except Exception as e:
        logger.error(f"Error creating learner: {str(e)}")
        return Response({"error": "Failed to create learner"}, status=500)
    parent_msg = None
    parent = None
    parent_data = data.get('parentData', {}) or {} # Default to empty dict
    if parent_data and parent_data.get('name') and parent_data.get('email'):
        p_name = parent_data['name'].strip()
        p_email = parent_data['email'].strip().lower()
        p_phone = parent_data.get('phone', '').strip()
        logger.info(f"Parent details: name={p_name}, email={p_email}, phone={p_phone}")
        try:
            if not CustomUser.objects.filter(email=p_email).exists():
                p_first, *p_last = p_name.split()
                parent = CustomUser(
                    username=p_email,
                    email=p_email,
                    first_name=p_first,
                    last_name=' '.join(p_last),
                    phone=p_phone,
                    role='parent',
                    must_change_password=True
                )
                parent.set_password(default_password)
                parent.full_clean()
                parent.save()
                parent_msg = "Parent account created"
                logger.info(f"Created new parent: ID={parent.id}, email={parent.email}")
            else:
                parent = CustomUser.objects.get(email=p_email)
                parent_msg = "Parent email already exists - linked to existing account"
                logger.info(f"Using existing parent: ID={parent.id}, email={parent.email}")
            # Link and verify
            if parent:
                user.parent = parent
                user.full_clean()
                user.save()
                # Verify link
                refreshed_user = CustomUser.objects.get(id=user.id)
                if refreshed_user.parent != parent:
                    raise ValueError("Link not saved")
                logger.info(f"Linked learner ID={user.id} to parent ID={parent.id}")
        except Exception as e:
            logger.error(f"Error linking parent: {str(e)}")
            return Response({"error": "Failed to link parent"}, status=500)
    return Response({
        "message": "Student created",
        "temporary_password": default_password,
        "parent_message": parent_msg
    }, status=201)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_parent(request):
    data = request.data
    name = data.get('name', '').strip()
    email = data.get('email', '').strip().lower()
    phone = data.get('phone', '').strip()
    if not name or not email:
        return Response({"error": "Name and email required for parent"}, status=400)
    if CustomUser.objects.filter(email=email).exists():
        return Response({"error": "Email already exists"}, status=400)
    first, *last = name.split()
    user = CustomUser.objects.create(
        username=email,
        email=email,
        first_name=first,
        last_name=' '.join(last),
        phone=phone,
        role='parent',
        must_change_password=True
    )
    default_password = SystemSetting.objects.get(key='default_password').value
    user.set_password(default_password)
    user.save()
    return Response({
        "message": "Parent created",
        "temporary_password": default_password
    }, status=201)
# Add this new view for parent dashboard to fetch children
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_children(request):
    if request.user.role != 'parent':
        return Response({"error": "Access denied"}, status=403)
    children = request.user.children.all()
    logger.info(f"Fetching children for parent ID={request.user.id}: Found {children.count()} children")
    serializer = UserSerializer(children, many=True)
    return Response(serializer.data)
# New view for admin to link existing learner to parent
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def link_parent(request):
    if request.user.role.upper() not in ['SUPER_ADMIN', 'ADMIN']:
        return Response({"error": "Access denied - admins only"}, status=403)
    data = request.data
    learner_id = data.get('learner_id')
    parent_id = data.get('parent_id')
    if not learner_id or not parent_id:
        return Response({"error": "learner_id and parent_id required"}, status=400)
    try:
        learner = CustomUser.objects.get(id=learner_id, role='learner')
        parent = CustomUser.objects.get(id=parent_id, role='parent')
        learner.parent = parent
        learner.full_clean()
        learner.save()
        # Verify
        refreshed_learner = CustomUser.objects.get(id=learner.id)
        if refreshed_learner.parent != parent:
            raise ValueError("Link not saved")
        logger.info(f"Linked learner ID={learner.id} to parent ID={parent.id} by admin")
        return Response({"message": "Linked successfully"}, status=200)
    except CustomUser.DoesNotExist:
        return Response({"error": "User not found or invalid role"}, status=404)
    except Exception as e:
        logger.error(f"Error in link_parent: {str(e)}")
        return Response({"error": "Linking failed"}, status=500)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_children_progress(request):
    if request.user.role != 'parent':
        return Response({"error": "Access denied"}, status=403)
    children = request.user.children.filter(role='learner').all() # Only learners
    logger.info(f"Fetching progress for parent ID={request.user.id}: Found {children.count()} children")
    serializer = StudentSerializer(children, many=True)
    return Response(serializer.data)
@api_view(['GET', 'POST'])
def list_create_learning_areas(request):
    if request.method == 'GET':
        # Allow any authenticated user (teachers, admins, etc.) to list learning areas
        if not request.user.is_authenticated:
            return Response({"error": "Authentication required"}, status=401)
   
        areas = LearningArea.objects.all().order_by('name')
        serializer = LearningAreaSerializer(areas, many=True)
        return Response(serializer.data)
    if request.method == 'POST':
        # Only admins can create new learning areas
        if not (request.user.is_authenticated and request.user.role.upper() in ['SUPER_ADMIN', 'ADMIN']):
            return Response({"error": "Admin privileges required"}, status=403)
   
        serializer = LearningAreaSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['DELETE'])
@permission_classes([IsSchoolAdmin])
def delete_learning_area(request, pk):
    try:
        area = LearningArea.objects.get(pk=pk)
        area.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    except LearningArea.DoesNotExist:
        return Response({"detail": "Subject not found"}, status=status.HTTP_404_NOT_FOUND)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_learning_areas(request):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    areas = request.user.enrolled_learning_areas.all().order_by('name')
    serializer = LearningAreaSerializer(areas, many=True)
    return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def available_learning_areas(request):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    if not request.user.class_stream:
        return Response({"error": "You must be assigned to a class first"}, status=403)
    areas = LearningArea.objects.filter(teacherallocation__class_stream=request.user.class_stream).distinct().order_by('name')
    serializer = LearningAreaSerializer(areas, many=True)
    return Response(serializer.data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def enroll_learning_area(request, area_id):
    if request.user.role != 'learner':
        return Response({"error": "Only learners can enroll"}, status=403)
    area = get_object_or_404(LearningArea, id=area_id)
    if request.user.enrolled_learning_areas.filter(id=area_id).exists():
        return Response({"message": "Already enrolled"}, status=200)
    if not request.user.class_stream:
        return Response({"error": "You must be assigned to a class first"}, status=403)
    if not TeacherAllocation.objects.filter(class_stream=request.user.class_stream, learning_area=area).exists():
        return Response({"error": "This subject is not available in your class"}, status=403)
    request.user.enrolled_learning_areas.add(area)
    request.user.save()
    return Response({"message": "Enrolled successfully"}, status=201)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_content(request, learning_area_id):
    area = get_object_or_404(LearningArea, id=learning_area_id)
    role_upper = request.user.role.upper()
    if role_upper == 'LEARNER':
        if not request.user.enrolled_learning_areas.filter(id=learning_area_id).exists():
            return Response({"error": "Not enrolled in this learning area"}, status=403)
        contents = LearningContent.objects.filter(learning_area=area).order_by('-created_at')
    elif role_upper == 'TEACHER':
        contents = LearningContent.objects.filter(learning_area=area, teacher=request.user).order_by('-created_at')
    elif role_upper in ['ADMIN', 'SUPER_ADMIN']:
        contents = LearningContent.objects.filter(learning_area=area).order_by('-created_at')
    else:
        return Response({"error": "Access denied"}, status=403)
    serializer = ContentSerializer(contents, many=True, context={'request': request})
    return Response(serializer.data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
@parser_classes([MultiPartParser, FormParser]) # JSONParser removed - frontend always sends FormData
def create_content(request):
    if request.user.role != 'teacher':
        return Response({"error": "Only teachers can create content"}, status=403)
    settings = get_system_settings()
    if not bool(settings.get('allow_teacher_content_creation', 'True') == 'True'):
        return Response({"error": "Teacher content creation is currently disabled"}, status=403)
    learning_area_id = request.data.get('learning_area')
    if not learning_area_id:
        return Response({"error": "learning_area required"}, status=400)
    try:
        learning_area = LearningArea.objects.get(id=learning_area_id)
    except LearningArea.DoesNotExist:
        return Response({"error": "Invalid learning area"}, status=400)
    if not TeacherAllocation.objects.filter(teacher=request.user, learning_area=learning_area).exists():
        return Response({"error": "You are not allocated to this learning area"}, status=403)
    serializer = ContentSerializer(data=request.data, context={'request': request})
    if serializer.is_valid():
        instance = serializer.save(teacher=request.user, learning_area=learning_area)
        return Response(ContentSerializer(instance, context={'request': request}).data, status=201)
   
    return Response(serializer.errors, status=400)
@api_view(['DELETE'])
@permission_classes([IsAuthenticated])
def delete_content(request, pk):
    if request.user.role != 'teacher':
        return Response({"error": "Access denied - only teachers can delete content"}, status=403)
    try:
        content = LearningContent.objects.get(pk=pk, teacher=request.user)
        content.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    except LearningContent.DoesNotExist:
        return Response(
            {"error": "Content not found or you do not have permission to delete it"},
            status=status.HTTP_404_NOT_FOUND
        )
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def content_progress(request, content_id):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    content = get_object_or_404(LearningContent, id=content_id)
    progress, created = ContentProgress.objects.get_or_create(user=request.user, content=content)
    if request.method == 'GET':
        serializer = ContentProgressSerializer(progress)
        return Response(serializer.data)
    elif request.method == 'POST':
        new_progress = request.data.get('progress')
        if new_progress is not None:
            try:
                progress.progress = min(max(int(new_progress), 0), 100)
                progress.completed = (progress.progress == 100)
                progress.save()
            except ValueError:
                return Response({"error": "Invalid progress value"}, status=400)
        serializer = ContentProgressSerializer(progress)
        return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_progress(request):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    # Quiz stats
    attempts = request.user.quiz_attempts.all()
    total_attempts = attempts.count()
    if total_attempts > 0:
        average_score_percent = sum((a.score / a.total * 100 if a.total > 0 else 0) for a in attempts) / total_attempts
    else:
        average_score_percent = 0
    attendance_records = request.user.attendance_records.all()
    total_days = attendance_records.count()
    present_days = attendance_records.filter(status='PRESENT').count()
    all_quizzes = Quiz.objects.filter(learning_area__in=request.user.enrolled_learning_areas.all())
    attempted_quiz_ids = attempts.values_list('quiz__id', flat=True)
    pending_quizzes = all_quizzes.exclude(id__in=attempted_quiz_ids).count()
    data = {
        "summary": {
            "quizStats": {
                "totalAttempts": total_attempts,
                "averageScorePercent": average_score_percent,
                "pendingQuizzes": pending_quizzes # Added for clarity, adjust if needed
            },
            "attendanceStats": {
                "totalDays": total_days,
                "presentDays": present_days
            }
        }
    }
    return Response(data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_quizzes(request):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    quizzes = Quiz.objects.filter(learning_area__in=request.user.enrolled_learning_areas.all()).order_by('-id')
    serializer = QuizSerializer(quizzes, many=True, context={'request': request})
    return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_allocations(request):
    if request.user.role != 'teacher':
        return Response({"error": "Access denied"}, status=403)
    allocations = TeacherAllocation.objects.filter(teacher=request.user).order_by('class_stream__grade__name', 'class_stream__stream_name', 'learning_area__name')
    serializer = TeacherAllocationSerializer(allocations, many=True)
    return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_attendance(request):
    if request.user.role.lower() != 'learner':
        return Response({"error": "Access denied"}, status=403)
    # Re-use the same StudentSerializer that is used for parents' children
    serializer = StudentSerializer(request.user)
    return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def teacher_my_classes(request):
    if request.user.role != 'teacher':
        return Response({"error": "Access denied"}, status=403)
    allocations = TeacherAllocation.objects.filter(teacher=request.user).order_by('class_stream__grade__name', 'class_stream__stream_name', 'learning_area__name')
    serializer = TeacherAllocationSerializer(allocations, many=True)
    return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_teacher_classes(request):
    """
    Returns list of classes the current teacher is assigned to.
    """
    user = request.user
    try:
        allocations = TeacherAllocation.objects.filter(teacher=user).order_by('class_stream__grade__name', 'class_stream__stream_name', 'learning_area__name')
        serializer = TeacherAllocationSerializer(allocations, many=True)
        formatted_data = [
            {
                "id": item['id'],
                "className": item['class_stream']['name'],
                "classStreamId": item['class_stream']['id'],
                "subject": item['learning_area']['name'],
                "subjectId": item['learning_area']['id']
            } for item in serializer.data
        ]
        return Response(formatted_data)
    except Exception as e:
        logger.error(f"Error fetching teacher classes: {str(e)}")
        return Response({"detail": "Unable to fetch classes"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def list_class_students(request, class_id):
    """
    Returns list of students (learners) in the specified class stream.
    """
    try:
        class_stream = ClassStream.objects.get(id=class_id)
        students = CustomUser.objects.filter(role='learner', class_stream=class_stream)
        serializer = StudentListSerializer(students, many=True)
        return Response(serializer.data)
    except ClassStream.DoesNotExist:
        return Response({"detail": "Class not found"}, status=status.HTTP_404_NOT_FOUND)
@api_view(['GET', 'POST'])
@permission_classes([IsSchoolAdmin]) # Updated
def list_create_allocations(request):
    if request.method == 'GET':
        allocations = TeacherAllocation.objects.all()
        serializer = TeacherAllocationSerializer(allocations, many=True)
        return Response(serializer.data)
    if request.method == 'POST':
        serializer = TeacherAllocationSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['DELETE'])
@permission_classes([IsSchoolAdmin]) # Updated
def delete_allocation(request, pk):
    try:
        allocation = TeacherAllocation.objects.get(pk=pk)
        allocation.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    except TeacherAllocation.DoesNotExist:
        return Response({"detail": "Allocation not found"}, status=status.HTTP_404_NOT_FOUND)
@api_view(['GET', 'POST'])
@permission_classes([IsSchoolAdmin]) # Updated
def list_create_class_streams(request):
    if request.method == 'GET':
        class_streams = ClassStream.objects.all()
        serializer = ClassStreamListSerializer(class_streams, many=True)
        return Response(serializer.data)
    if request.method == 'POST':
        serializer = ClassStreamSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['DELETE'])
@permission_classes([IsSchoolAdmin]) # Updated
def delete_class_stream(request, pk):
    try:
        class_stream = ClassStream.objects.get(pk=pk)
        class_stream.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    except ClassStream.DoesNotExist:
        return Response({"detail": "Class not found"}, status=status.HTTP_404_NOT_FOUND)
# New view for recording attendance
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def record_attendance(request):
    data = request.data
    date_str = data.get('date')
    records = data.get('records')
    if not date_str or not records:
        return Response({"error": "date and records are required"}, status=400)
    try:
        date_obj = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
        date_only = date_obj.date()
    except ValueError:
        return Response({"error": "Invalid date format"}, status=400)
    created_count = 0
    for rec in records:
        try:
            learner = CustomUser.objects.get(id=rec['learnerId'], role='learner')
            attendance, created = Attendance.objects.update_or_create(
                user=learner,
                date=date_only,
                defaults={
                    'status': rec.get('status', 'PRESENT'),
                    'remarks': rec.get('remarks', '')
                }
            )
            created_count += 1
        except CustomUser.DoesNotExist:
            pass # Skip invalid learners
        except Exception as e:
            logger.error(f"Error creating attendance for learner {rec.get('learnerId')}: {str(e)}")
    if created_count == 0:
        return Response({"error": "No valid attendance records created"}, status=400)
    return Response({"message": "Attendance recorded successfully"}, status=201)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def current_user(request):
    """
    Returns the current authenticated user's details.
    """
    serializer = UserSerializer(request.user)
    data = serializer.data
    data['teacherProfileId'] = request.user.id # Assuming ID is the profile ID
    return Response(data)
# New view to get attendance for a class on a specific date
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_class_attendance(request, class_id):
    if request.user.role != 'teacher':
        return Response({"error": "Access denied - teachers only"}, status=403)
    try:
        class_stream = ClassStream.objects.get(id=class_id)
    except ClassStream.DoesNotExist:
        return Response({"detail": "Class not found"}, status=404)
    # Check if teacher is allocated to this class
    if not TeacherAllocation.objects.filter(teacher=request.user, class_stream=class_stream).exists():
        return Response({"error": "You are not allocated to this class"}, status=403)
    date_str = request.query_params.get('date')
    if date_str:
        try:
            date_only = datetime.strptime(date_str, '%Y-%m-%d').date()
        except ValueError:
            return Response({"error": "Invalid date format. Use YYYY-MM-DD"}, status=400)
    else:
        date_only = date.today()
    students = CustomUser.objects.filter(role='learner', class_stream=class_stream)
    attendances = Attendance.objects.filter(user__in=students, date=date_only)
    data = {str(att.user.id): att.status for att in attendances}
    return Response(data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_quiz(request):
    if request.user.role.upper() not in ['TEACHER', 'ADMIN', 'SUPER_ADMIN']:
        return Response({"error": "Access denied - teachers and admins only"}, status=403)
    data = request.data
    title = data.get('title')
    description = data.get('description', '')
    learning_area_id = data.get('learning_area')
    allow_retakes = data.get('allow_retakes', True)
    teacher_id = data.get('teacher') # May be sent from frontend
    questions_data = data.get('questions', [])
    if not title or not learning_area_id or not questions_data:
        return Response({"error": "Missing required fields: title, learning_area, or questions"}, status=400)
    # Optional security check if teacher is sent (prevent spoofing)
    if teacher_id is not None and request.user.id != teacher_id:
        return Response({"error": "Cannot create quiz for another teacher"}, status=403)
    try:
        learning_area = LearningArea.objects.get(id=learning_area_id)
    except LearningArea.DoesNotExist:
        return Response({"error": "Learning area not found"}, status=404)
    try:
        quiz = Quiz.objects.create(
            title=title,
            description=description,
            allow_retakes=allow_retakes,
            learning_area=learning_area,
            teacher=request.user # Always use authenticated user
        )
        for q_data in questions_data:
            question = Question.objects.create(
                quiz=quiz,
                text=q_data['text'],
                type=q_data['type']
            )
            for opt_data in q_data['options']:
                Option.objects.create(
                    question=question,
                    text=opt_data['text'],
                    is_correct=opt_data['isCorrect']
                )
        serializer = QuizSerializer(quiz)
        return Response(serializer.data, status=201)
    except Exception as e:
        logger.error(f"Error creating quiz: {str(e)}")
        return Response({"error": "Failed to create quiz"}, status=500)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def teacher_quizzes(request, subject_id):
    role = request.user.role.upper()
    if role not in ['TEACHER', 'ADMIN', 'SUPER_ADMIN']:
        return Response({"error": "Access denied - teachers and admins only"}, status=403)
    try:
        learning_area = LearningArea.objects.get(id=subject_id)
    except LearningArea.DoesNotExist:
        return Response({"error": "Learning area not found"}, status=404)
    # For teachers, only show their own quizzes
    # For admins, show all quizzes for the subject
    if role == 'TEACHER':
        quizzes = Quiz.objects.filter(
            teacher=request.user,
            learning_area=learning_area
        )
    else: # ADMIN or SUPER_ADMIN
        quizzes = Quiz.objects.filter(
            learning_area=learning_area
        )
    quizzes = quizzes.annotate(
        question_count=Count('questions'),
        attempt_count=Count('attempts')
    ).order_by('-created_at')
    data = [
        {
            "id": quiz.id,
            "title": quiz.title,
            "description": quiz.description or "",
            "createdAt": quiz.created_at.isoformat(),
            "allow_retakes": quiz.allow_retakes,
            "_count": {
                "questions": quiz.question_count,
                "attempts": quiz.attempt_count
            }
        } for quiz in quizzes
    ]
    return Response(data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def student_quizzes(request, area_id):
    """
    Get all quizzes for a specific learning area that the student is enrolled in.
    """
    if request.user.role.upper() != 'LEARNER':
        return Response({"error": "Access denied - learners only"}, status=403)
    try:
        learning_area = LearningArea.objects.get(id=area_id)
    except LearningArea.DoesNotExist:
        return Response({"error": "Learning area not found"}, status=404)
    # Check enrollment
    if not request.user.enrolled_learning_areas.filter(id=area_id).exists():
        return Response({"error": "You are not enrolled in this subject"}, status=403)
    quizzes = Quiz.objects.filter(learning_area=learning_area).annotate(
        question_count=Count('questions'),
        attempt_count=Count('attempts')
    ).order_by('-created_at')
    data = [
        {
            "id": quiz.id,
            "title": quiz.title,
            "description": quiz.description or "",
            "createdAt": quiz.created_at.isoformat(),
            "_count": {
                "questions": quiz.question_count,
                "attempts": quiz.attempt_count
            }
        } for quiz in quizzes
    ]
    return Response(data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def enroll_student_in_area(request, student_id, area_id):
    if request.user.role.upper() not in ['TEACHER', 'ADMIN', 'SUPER_ADMIN']:
        return Response({"error": "Access denied"}, status=403)
    student = get_object_or_404(CustomUser, id=student_id, role='learner')
    area = get_object_or_404(LearningArea, id=area_id)
    if student.enrolled_learning_areas.filter(id=area_id).exists():
        return Response({"message": "Already enrolled"}, status=200)
    student.enrolled_learning_areas.add(area)
    student.save()
    return Response({"message": f"Student {student.username} enrolled in {area.name}"}, status=201)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_quiz(request, quiz_id):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    quiz = get_object_or_404(Quiz, id=quiz_id)
    # Check if enrolled in the learning area
    if not request.user.enrolled_learning_areas.filter(id=quiz.learning_area.id).exists():
        return Response({"error": "Not enrolled"}, status=403)
    questions_data = []
    for q in quiz.questions.all():
        options_data = [{'id': o.id, 'text': o.text} for o in q.options.all()]
        questions_data.append({
            'id': q.id,
            'text': q.text,
            'type': q.type,
            'options': options_data
        })
    return Response({'questions': questions_data})
# api/views.py - Updated submit_quiz_attempt view with strict retake enforcement
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def submit_quiz_attempt(request, quiz_id):
    if request.user.role != 'learner':
        return Response({"error": "Access denied"}, status=403)
    quiz = get_object_or_404(Quiz, id=quiz_id)
    # Enrollment check
    if not request.user.enrolled_learning_areas.filter(id=quiz.learning_area.id).exists():
        return Response({"error": "Not enrolled in this subject"}, status=403)
    # STRICT RETAKE ENFORCEMENT
    existing_attempt = QuizAttempt.objects.filter(quiz=quiz, user=request.user).exists()
    if existing_attempt and not quiz.allow_retakes:
        return Response({
            "error": "Retakes are not allowed for this quiz. You have already submitted an attempt."
        }, status=403)
    data = request.data
    learner_id = data.get('learnerId')
    if learner_id != request.user.id:
        return Response({"error": "Invalid learner"}, status=400)
    answers = data.get('answers', {})
    questions = quiz.questions.all()
    if len(answers) != questions.count():
        return Response({"error": "Incomplete answers - all questions must be answered"}, status=400)
    score = 0
    total = questions.count()
    for q in questions:
        selected_opt_id = answers.get(str(q.id))
        if selected_opt_id:
            try:
                opt = Option.objects.get(id=selected_opt_id, question=q)
                if opt.is_correct:
                    score += 1
            except Option.DoesNotExist:
                pass # Invalid option selected - no point
    percentage = (score / total * 100) if total > 0 else 0
    # Create the attempt (this will be the only one if allow_retakes=False)
    attempt = QuizAttempt.objects.create(
        quiz=quiz,
        user=request.user,
        score=score,
        total=total
    )
    return Response({
        "message": "Quiz submitted successfully!",
        "score": score,
        "total": total,
        "percentage": round(percentage),
        "retake_allowed": quiz.allow_retakes and not existing_attempt # Info for frontend
    })
@api_view(['GET', 'PATCH', 'DELETE'])
@permission_classes([IsSchoolAdmin])
def user_detail(request, pk):
    try:
        user = CustomUser.objects.get(pk=pk)
    except CustomUser.DoesNotExist:
        return Response({"error": "User not found"}, status=404)
    if request.method == 'GET':
        serializer = UserSerializer(user)
        return Response(serializer.data)
    elif request.method == 'PATCH':
        data = request.data
        updated_fields = []
        # Update name
        if 'name' in data:
            name_parts = data['name'].strip().split()
            if name_parts:
                user.first_name = name_parts[0]
                user.last_name = ' '.join(name_parts[1:]) if len(name_parts) > 1 else ''
                updated_fields.extend(['first_name', 'last_name'])
        # Update email for non-learners
        if 'email' in data and data['email']:
            if user.role.lower() in ['teacher', 'admin', 'super_admin', 'parent']:
                user.email = data['email'].strip().lower()
                updated_fields.append('email')
            else:
                return Response({"error": "Email update not allowed for learners"}, status=400)
        # Update grade_level for learners
        if 'gradeLevelId' in data:
            if user.role.lower() == 'learner':
                grade_id = data['gradeLevelId']
                if grade_id:
                    try:
                        Grade.objects.get(code=grade_id)
                    except Grade.DoesNotExist:
                        return Response({"error": "Invalid grade code"}, status=400)
                user.grade_level = grade_id or None
                updated_fields.append('grade_level')
        # Update phone for teachers/parents/admins/super_admins
        if 'phone' in data:
            if user.role.lower() in ['teacher', 'parent', 'admin', 'super_admin']:
                user.phone = data['phone'].strip()
                updated_fields.append('phone')
        # NEW: Update class_stream for learners only
        if 'class_stream' in data:
            if user.role.lower() != 'learner':
                return Response({"error": "Class assignment only allowed for students"}, status=400)
       
            class_id = data['class_stream']
            if class_id: # Non-empty string or number
                try:
                    class_stream_obj = ClassStream.objects.get(id=class_id)
                    user.class_stream = class_stream_obj
                    updated_fields.append('class_stream')
                except ClassStream.DoesNotExist:
                    return Response({"error": "Invalid class stream ID"}, status=400)
                except ValueError:
                    return Response({"error": "Invalid class stream ID format"}, status=400)
            else: # null or empty - remove assignment
                user.class_stream = None
                updated_fields.append('class_stream')
        # Handle parent linking for learners
        if user.role.lower() == 'learner' and 'parentData' in data:
            parent_details = data['parentData']
       
            if parent_details and parent_details.get('name') and parent_details.get('email'):
                p_name = parent_details['name'].strip()
                p_email = parent_details['email'].strip().lower()
                p_phone = parent_details.get('phone', '').strip()
           
                try:
                    if CustomUser.objects.filter(email=p_email, role='parent').exists():
                        parent = CustomUser.objects.get(email=p_email, role='parent')
                    else:
                        p_first, *p_last = p_name.split()
                        parent = CustomUser(
                            username=p_email,
                            email=p_email,
                            first_name=p_first,
                            last_name=' '.join(p_last) if p_last else '',
                            phone=p_phone,
                            role='parent',
                            must_change_password=True
                        )
                        default_password = SystemSetting.objects.get(key='default_password').value
                        parent.set_password(default_password)
                        parent.save()
               
                    user.parent = parent
                    updated_fields.append('parent')
               
                except Exception as e:
                    logger.error(f"Error linking parent: {str(e)}")
                    return Response({"error": f"Failed to link parent: {str(e)}"}, status=400)
       
            elif 'removeParent' in data and data['removeParent']:
                user.parent = None
                updated_fields.append('parent')
        if not updated_fields:
            return Response({"error": "No valid fields provided for update"}, status=400)
        try:
            user.save(update_fields=updated_fields)
            return Response(UserSerializer(user).data, status=200)
        except Exception as e:
            logger.error(f"Error updating user: {str(e)}")
            return Response({"error": f"Failed to update user: {str(e)}"}, status=400)
    elif request.method == 'DELETE':
        if user == request.user:
            return Response({"error": "Cannot delete yourself"}, status=400)
        elif user.role == "super_admin":
            return Response({"error": "Cannot delete super admin"}, status=400)
        user.delete()
        return Response({"message": "User deleted successfully"}, status=204)
@api_view(['DELETE'])
@permission_classes([IsAuthenticated])
def delete_quiz(request, pk):
    if request.user.role != 'teacher':
        return Response({"error": "Access denied - only teachers can delete content"}, status=403)
    try:
        quiz = Quiz.objects.get(pk=pk, teacher=request.user)
        quiz.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    except Quiz.DoesNotExist:
        return Response(
            {"error": "Quiz not found or you do not have permission to delete it"},
            status=status.HTTP_404_NOT_FOUND
        )
@api_view(['GET', 'PUT', 'DELETE'])
@permission_classes([IsAuthenticated])
def quiz_detail(request, pk):
    quiz = get_object_or_404(Quiz, pk=pk)
    is_owner = (request.user == quiz.teacher)
    is_admin = request.user.role.upper() in ['SUPER_ADMIN', 'ADMIN']
    is_learner = (request.user.role == 'learner')
    if request.method == 'GET':
        if is_learner:
            if not request.user.enrolled_learning_areas.filter(id=quiz.learning_area.id).exists():
                return Response({"error": "Not enrolled"}, status=403)
            # For learners: hide isCorrect
            questions_data = []
            for q in quiz.questions.all():
                options_data = [{'id': o.id, 'text': o.text} for o in q.options.all()]
                questions_data.append({
                    'id': q.id,
                    'text': q.text,
                    'type': q.type,
                    'options': options_data
                })
            data = {
                'id': quiz.id,
                'title': quiz.title,
                'description': quiz.description or "",
                'questions': questions_data
            }
            return Response(data)
        elif is_owner or is_admin:
            # For teachers/admins: include isCorrect
            questions_data = []
            for q in quiz.questions.all():
                options_data = [{'id': o.id, 'text': o.text, 'isCorrect': o.is_correct} for o in q.options.all()]
                questions_data.append({
                    'id': q.id,
                    'text': q.text,
                    'type': q.type,
                    'options': options_data
                })
            data = {
                'id': quiz.id,
                'title': quiz.title,
                'description': quiz.description or "",
                'allow_retakes': quiz.allow_retakes,
                'questions': questions_data
            }
            return Response(data)
        else:
            return Response({"error": "Access denied"}, status=403)
    if not (is_owner or is_admin):
        return Response({"error": "You do not have permission to edit/delete this quiz"}, status=403)
    if request.method == 'PUT':
        data = request.data
        title = data.get('title')
        description = data.get('description', '')
        questions_data = data.get('questions', [])
        if not title or not questions_data:
            return Response({"error": "Missing required fields"}, status=400)
        quiz.title = title
        quiz.description = description
        quiz.allow_retakes = data.get('allow_retakes', quiz.allow_retakes)
        quiz.save()
        quiz.questions.all().delete()
        for q_data in questions_data:
            question = Question.objects.create(
                quiz=quiz,
                text=q_data['text'],
                type=q_data['type']
            )
            for opt_data in q_data['options']:
                Option.objects.create(
                    question=question,
                    text=opt_data['text'],
                    is_correct=opt_data['isCorrect']
                )
        return Response({"message": "Quiz updated successfully"})
    if request.method == 'DELETE':
        quiz.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
# api/views.py (strict: CSV import ALWAYS uses the admin-set system default password, no override allowed)
@api_view(['POST'])
@permission_classes([IsSchoolAdmin])
def import_learners_csv(request):
    csv_content = request.data.get('csvContent')
    # STRICT: Always use the current system default set by admin - ignore any provided defaultPassword
    default_password = SystemSetting.objects.get(key='default_password').value
    logger.info(f"CSV import using admin-set system default password")
    if not csv_content:
        return Response({"error": "No CSV content provided"}, status=400)
    success_count = 0
    errors = []
    try:
        reader = csv.DictReader(StringIO(csv_content))
    
        with transaction.atomic():
            for row_num, row in enumerate(reader, start=2): # start=2 for data rows
                try:
                    name = row.get('Name', '').strip()
                    admission = row.get('Admission', '').strip()
                    grade = row.get('Grade', '').strip()
                    parent_name = row.get('ParentName', '').strip()
                    parent_email = row.get('ParentEmail', '').strip().lower()
                    parent_phone = row.get('ParentPhone', '').strip()
                
                    if not name or not admission or not grade:
                        errors.append(f"Row {row_num}: Missing required fields (Name, Admission, or Grade)")
                        continue
                
                    name_parts = name.split()
                    if not name_parts:
                        errors.append(f"Row {row_num}: Invalid name")
                        continue
                
                    first = name_parts[0]
                    last = ' '.join(name_parts[1:]) if len(name_parts) > 1 else ''
                    username = re.sub(r'\s+', '-', admission).upper()  # ← normalised
                    learner_email = f"{first.lower()}.{admission.lower()}@bethel.school"
                
                    if CustomUser.objects.filter(username=username).exists():
                        errors.append(f"Row {row_num}: Admission {username} already exists")
                        continue
                    if CustomUser.objects.filter(email=learner_email).exists():
                        errors.append(f"Row {row_num}: Email {learner_email} already exists")
                        continue
                
                    learner = CustomUser.objects.create(
                        username=username,
                        email=learner_email,
                        first_name=first,
                        last_name=last,
                        role='learner',
                        grade_level=grade,
                        must_change_password=True
                    )
                    learner.set_password(default_password)
                    learner.save()
                    logger.info(f"Created learner {learner.email} with admin-set default password")
                
                    parent = None
                    if parent_email:
                        if not parent_name:
                            parent_name = f"Parent of {name}"
                        p_parts = parent_name.split()
                        p_first = p_parts[0]
                        p_last = ' '.join(p_parts[1:]) if len(p_parts) > 1 else ''
                    
                        parent, created = CustomUser.objects.get_or_create(
                            email=parent_email,
                            defaults={
                                'username': parent_email,
                                'first_name': p_first,
                                'last_name': p_last,
                                'phone': parent_phone,
                                'role': 'parent',
                                'must_change_password': True
                            }
                        )
                        if created:
                            parent.set_password(default_password) # Always admin-set default
                            parent.save()
                            logger.info(f"Created new parent {parent.email} with admin-set default password")
                        learner.parent = parent
                        learner.save()
                
                    success_count += 1
                except Exception as e:
                    errors.append(f"Row {row_num}: {str(e)}")
                    logger.error(f"CSV import error on row {row_num}: {str(e)}")
    
        return Response({
            "successCount": success_count,
            "errors": errors,
            "note": f"All new users created with current system default password set by admin"
        }, status=200)
    except Exception as e:
        logger.error(f"CSV import processing failed: {str(e)}")
        return Response({"error": f"CSV processing failed: {str(e)}"}, status=500)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_class_stream(request):
    """
    Returns the current class stream for the authenticated learner.
    """
    if request.user.role.upper() != 'LEARNER':
        return Response({"error": "Access denied - learners only"}, status=403)
    if not request.user.class_stream:
        return Response({"detail": "No class assigned yet"}, status=200)
    stream = request.user.class_stream
    # Get subjects taught in this class
    allocations = TeacherAllocation.objects.filter(class_stream=stream)
    subjects = [
        {"id": alloc.learning_area.id, "name": alloc.learning_area.name}
        for alloc in allocations
    ]
    # Compute the display name (most school apps combine grade + stream_name)
    class_name = ""
    if hasattr(stream, 'grade') and stream.grade:
        class_name = stream.grade.name
        if hasattr(stream, 'stream_name') and stream.stream_name:
            class_name += f" {stream.stream_name}"
    else:
        # Fallback if no grade/stream_name
        class_name = "My Class"
    data = {
        "id": stream.id,
        "name": class_name.strip() or "My Class",
        "grade": stream.grade.name if hasattr(stream, 'grade') and stream.grade else None,
        "gradeCode": stream.grade.code if hasattr(stream, 'grade') and stream.grade else None,
        "studentCount": CustomUser.objects.filter(role='learner', class_stream=stream).count(),
        "subjects": subjects,
    }
    return Response(data)
# api/views.py
# Add these imports at the top if not already present
from django.db.models import Q
# Add these views at the end of the file, before the closing
# Admin settings endpoints
@api_view(['POST'])
@permission_classes([IsSchoolAdmin])
def reset_user_password(request):
    """
    Admin endpoint to reset any user's password
    """
    data = request.data
    user_id = data.get('user_id')
    new_password = data.get('new_password')
    force_change = data.get('force_change', True)
    if not user_id or not new_password:
        return Response({"error": "user_id and new_password are required"}, status=400)
    try:
        user = CustomUser.objects.get(id=user_id)
   
        # Don't allow admin to reset their own password through this endpoint
        if user == request.user:
            return Response({"error": "Use change-password endpoint for your own password"}, status=400)
   
        user.set_password(new_password)
        user.must_change_password = force_change
        user.save()
   
        logger.info(f"Password reset for user {user.email} by admin {request.user.email}")
     
   
        # Check B: Role Hierarchy (Admin cannot touch Super Admin)
        if request.user.role == 'admin' and user.role == 'super_admin':
            return Response({"error": "Admins cannot change Super Admin passwords"}, status=status.HTTP_403_FORBIDDEN)
   
        return Response({
            "message": f"Password reset successfully for {user.email}",
            "user": UserSerializer(user).data
        }, status=200)
   
    except CustomUser.DoesNotExist:
        return Response({"error": "User not found"}, status=404)
    except Exception as e:
        logger.error(f"Error resetting password: {str(e)}")
        return Response({"error": "Failed to reset password"}, status=500)
@api_view(['GET', 'POST'])
@permission_classes([IsSchoolAdmin])
def admin_settings(request):
    if request.method == 'GET':
        return Response(get_system_settings())
    elif request.method == 'POST':
        data = request.data
        # Update each setting if provided
        if 'allow_student_registration' in data:
            update_system_setting('allow_student_registration', str(data['allow_student_registration']))
        if 'allow_teacher_content_creation' in data:
            update_system_setting('allow_teacher_content_creation', str(data['allow_teacher_content_creation']))
        if 'default_password' in data:
            update_system_setting('default_password', data['default_password'])
        if 'session_timeout' in data:
            update_system_setting('session_timeout', str(data['session_timeout']))
        if 'max_file_size' in data:
            update_system_setting('max_file_size', str(data['max_file_size']))
        if 'allowed_file_types' in data:
            update_system_setting('allowed_file_types', data['allowed_file_types'])
      
        logger.info(f"System settings updated by {request.user.email}")
        return Response({"message": "Settings saved successfully", "settings": get_system_settings()})
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def system_stats(request):
    """
    Get system statistics for admin dashboard
    """
    try:
        total_users = CustomUser.objects.count()
        admins = CustomUser.objects.filter(role='admin').count()
        teachers = CustomUser.objects.filter(role='teacher').count()
        learners = CustomUser.objects.filter(role='learner').count()
        parents = CustomUser.objects.filter(role='parent').count()
   
        # Recent activity
        recent_users = CustomUser.objects.order_by('-date_joined')[:5]
   
        # User status counts
        password_reset_required = CustomUser.objects.filter(must_change_password=True).count()
   
        # Learning area counts
        total_learning_areas = LearningArea.objects.count()
        total_content = LearningContent.objects.count()
        total_quizzes = Quiz.objects.count()
   
        # Class stream counts
        total_classes = ClassStream.objects.count()
        total_allocations = TeacherAllocation.objects.count()
   
        return Response({
            "total_users": total_users,
            "by_role": {
                "admins": admins,
                "teachers": teachers,
                "learners": learners,
                "parents": parents
            },
            "user_status": {
                "active": total_users - password_reset_required,
                "password_reset_required": password_reset_required
            },
            "system_resources": {
                "learning_areas": total_learning_areas,
                "content": total_content,
                "quizzes": total_quizzes,
                "classes": total_classes,
                "allocations": total_allocations
            },
            "recent_users": UserSerializer(recent_users, many=True).data
        })
    except Exception as e:
        logger.error(f"Error getting system stats: {str(e)}")
        return Response({"error": "Failed to get system statistics"}, status=500)
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def activity_logs(request):
    """
    Get recent user activity (primarily logins) for admin monitoring.
    """
    try:
        # Get recent successful logins (based on last_login)
        recent_logins = CustomUser.objects.filter(
            last_login__isnull=False
        ).order_by('-last_login')[:20]
        logs = []
        for user in recent_logins:
            logs.append({
                "user_name": user.get_full_name().strip() or user.email or "Unknown",
                "activity": "User login",
                "timestamp": user.last_login.isoformat() if user.last_login else None,
            })
        # You can extend this later with more activity types (password changes, content creation, etc.)
        return Response({
            "logs": logs,
            "total": len(logs)
        })
    except Exception as e:
        logger.error(f"Error fetching activity logs: {str(e)}")
        return Response({"error": "Failed to fetch activity logs"}, status=500)
@api_view(['POST'])
@permission_classes([IsSchoolAdmin])
def bulk_user_actions(request):
    """
    Perform bulk actions on users (activate, deactivate, reset password)
    """
    data = request.data
    action = data.get('action')
    user_ids = data.get('user_ids', [])
    if not action or not user_ids:
        return Response({"error": "action and user_ids are required"}, status=400)
    try:
        users = CustomUser.objects.filter(id__in=user_ids)
   
        if action == 'reset_password':
            # Reset passwords for selected users
            password = data.get('password', SystemSetting.objects.get(key='default_password').value)
            force_change = data.get('force_change', True)
       
            for user in users:
                user.set_password(password)
                user.must_change_password = force_change
                user.save()
       
            return Response({
                "message": f"Passwords reset for {users.count()} users",
                "count": users.count()
            })
       
        elif action == 'deactivate':
            # Deactivate users
            users.update(is_active=False)
            return Response({
                "message": f"{users.count()} users deactivated",
                "count": users.count()
            })
       
        elif action == 'activate':
            # Activate users
            users.update(is_active=True)
            return Response({
                "message": f"{users.count()} users activated",
                "count": users.count()
            })
       
        elif action == 'require_password_change':
            # Require password change on next login
            users.update(must_change_password=True)
            return Response({
                "message": f"{users.count()} users will be required to change password on next login",
                "count": users.count()
            })
       
        else:
            return Response({"error": f"Invalid action: {action}"}, status=400)
       
    except Exception as e:
        logger.error(f"Error in bulk user actions: {str(e)}")
        return Response({"error": "Failed to perform bulk action"}, status=500)
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def export_users_csv(request):
    """
    Export users data to CSV format
    """
    try:
        users = CustomUser.objects.all().order_by('role', 'last_name', 'first_name')
  
        # Create CSV data
        import csv
        from io import StringIO
  
        output = StringIO()
        writer = csv.writer(output)
  
        # Write header
        writer.writerow([
            'ID', 'First Name', 'Last Name', 'Email', 'Username',
            'Role', 'Phone', 'Grade Level', 'Parent Email',
            'Class Stream', 'Must Change Password', 'Date Joined', 'Last Login'
        ])
  
        # Write data
        for user in users:
            parent_email = user.parent.email if user.parent else ''
            class_stream = ''
            if user.class_stream:
                if user.class_stream.grade:
                    class_stream = user.class_stream.grade.name
                if user.class_stream.stream_name:
                    class_stream += f" {user.class_stream.stream_name}"
      
            writer.writerow([
                user.id,
                user.first_name,
                user.last_name,
                user.email,
                user.username,
                user.role,
                user.phone or '',
                user.grade_level or '',
                parent_email,
                class_stream.strip(),
                'Yes' if user.must_change_password else 'No',
                user.date_joined.strftime('%Y-%m-%d %H:%M:%S') if user.date_joined else '',
                user.last_login.strftime('%Y-%m-%d %H:%M:%S') if user.last_login else ''
            ])
  
        csv_data = output.getvalue()
        output.close()
  
        return Response({
            "csv_data": csv_data,
            "filename": f"users_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        })
  
    except Exception as e:
        logger.error(f"Error exporting users to CSV: {str(e)}")
        return Response({"error": "Failed to export users data"}, status=500)
@api_view(['POST'])
@permission_classes([IsSchoolAdmin])
def send_bulk_notifications(request):
    """
    Send notifications to multiple users (email notifications)
    """
    data = request.data
    user_ids = data.get('user_ids', [])
    subject = data.get('subject', '')
    message = data.get('message', '')
    if not subject or not message:
        return Response({"error": "subject and message are required"}, status=400)
    if not user_ids:
        return Response({"error": "user_ids are required"}, status=400)
    try:
        users = CustomUser.objects.filter(id__in=user_ids)
   
        # In a real application, you would send emails here
        # For now, just log the action
        logger.info(f"Bulk notification sent by {request.user.email}:")
        logger.info(f"Subject: {subject}")
        logger.info(f"Message: {message}")
        logger.info(f"Recipients: {[user.email for user in users]}")
   
        return Response({
            "message": f"Notification sent to {users.count()} users",
            "count": users.count(),
            "recipients": [{"id": user.id, "email": user.email} for user in users]
        })
   
    except Exception as e:
        logger.error(f"Error sending bulk notifications: {str(e)}")
        return Response({"error": "Failed to send notifications"}, status=500)
@api_view(['POST'])
@permission_classes([IsSchoolAdmin])
def create_admin_user(request):
    """
    Create a new admin user (for super_admin only)
    """
    if request.user.role.upper() != 'SUPER_ADMIN':
        return Response({"error": "Only super admin can create new admin users"}, status=403)
    data = request.data
    name = data.get('name', '').strip()
    email = data.get('email', '').strip().lower()
    phone = data.get('phone', '').strip()
    if not name or not email:
        return Response({"error": "Name and email required for admin"}, status=400)
    if CustomUser.objects.filter(email=email).exists():
        return Response({"error": "Email already exists"}, status=400)
    first, *last = name.split()
    user = CustomUser.objects.create(
        username=email,
        email=email,
        first_name=first,
        last_name=' '.join(last) if last else '',
        phone=phone,
        role='admin',
        must_change_password=True
    )
    default_password = SystemSetting.objects.get(key='default_password').value
    user.set_password(default_password)
    user.save()
    return Response({
        "message": "Admin user created successfully",
        "temporary_password": default_password,
        "user": UserSerializer(user).data
    }, status=201)
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def audit_logs(request):
    """
    Get audit logs for admin review
    """
    try:
        # This is a simplified version - in production, you'd have a proper AuditLog model
        logs = []
   
        # Recent content creation
        recent_content = LearningContent.objects.order_by('-created_at')[:10]
        for content in recent_content:
            logs.append({
                "id": content.id,
                "action": "CREATE_CONTENT",
                "user_id": content.teacher.id if content.teacher else None,
                "user_name": content.teacher.get_full_name() if content.teacher else None,
                "resource_type": "LearningContent",
                "resource_id": content.id,
                "details": f"Created content: {content.title},{content.teacher.get_full_name()}",
                "timestamp": content.created_at.isoformat(),
                "ip_address": None
            })
   
        # Recent user creations
        recent_users = CustomUser.objects.order_by('-date_joined')[:10]
        for user in recent_users:
            logs.append({
                "id": user.id,
                "action": "CREATE_USER",
                "user_id": None, # Created by system/admin
                "user_name": None,
                "resource_type": "User",
                "resource_id": user.id,
                "details": f"Created user: {user.email} ({user.role})",
                "timestamp": user.date_joined.isoformat(),
                "ip_address": None
            })
   
        # Sort logs by timestamp
        logs.sort(key=lambda x: x['timestamp'], reverse=True)
   
        return Response({
            "logs": logs[:50], # Limit to 50 most recent
            "total": len(logs)
        })
    except Exception as e:
        logger.error(f"Error getting audit logs: {str(e)}")
        return Response({"error": "Failed to get audit logs"}, status=500)
# api/views.py (add this new view)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def quiz_attempts(request, quiz_id):
    """
    Get all attempts for a specific quiz (teacher/admin only)
    Returns: list of {user: {id, name}, score, total, percentage}
    """
    if request.user.role.upper() not in ['TEACHER', 'ADMIN', 'SUPER_ADMIN']:
        return Response({"error": "Access denied - teachers and admins only"}, status=403)
    try:
        quiz = Quiz.objects.get(id=quiz_id)
    except Quiz.DoesNotExist:
        return Response({"error": "Quiz not found"}, status=404)
    # Security: Teachers can only see attempts for their own quizzes
    if request.user.role.upper() == 'TEACHER' and quiz.teacher != request.user:
        return Response({"error": "You do not have permission to view attempts for this quiz"}, status=403)
    attempts = QuizAttempt.objects.filter(quiz=quiz).select_related('user').order_by('user__first_name', 'user__last_name')
    data = []
    for attempt in attempts:
        data.append({
            "user": {
                "id": attempt.user.id,
                "name": attempt.user.get_full_name() or attempt.user.email,
                "username": attempt.user.username
            },
            "score": attempt.score,
            "total": attempt.total,
            "percentage": round(attempt.score / attempt.total * 100) if attempt.total > 0 else 0
        })
    return Response(data)
# Add these imports if not already present
from django.db.models import Avg, Count
# Add this new view at the end of the file
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def admin_quiz_analytics(request):
    """
    Admin analytics for quiz results: shows general quiz results with teacher info.
    Returns list of quizzes with teacher, attempt count, average score/percentage.
    """
    quizzes = Quiz.objects.all().annotate(
        attempt_count=Count('attempts'),
        avg_score=Avg('attempts__score')
    ).order_by('-created_at')
    data = []
    for quiz in quizzes:
        question_count = quiz.questions.count()
        avg_percentage = (quiz.avg_score / question_count * 100) if question_count and quiz.avg_score is not None else 0
    
        data.append({
            "id": quiz.id,
            "title": quiz.title,
            "description": quiz.description or "",
            "learning_area": {
                "id": quiz.learning_area.id,
                "name": quiz.learning_area.name
            } if quiz.learning_area else None,
            "teacher": {
                "id": quiz.teacher.id,
                "name": quiz.teacher.get_full_name() or quiz.teacher.email
            } if quiz.teacher else {"id": None, "name": "Unknown"},
            "attempt_count": quiz.attempt_count,
            "avg_score": round(quiz.avg_score or 0, 2),
            "avg_percentage": round(avg_percentage),
            "question_count": question_count,
            "created_at": quiz.created_at.isoformat()
        })
    return Response({
        "quizzes": data,
        "total_quizzes": quizzes.count(),
        "total_attempts": sum(q.attempt_count for q in quizzes)
    })
@api_view(['GET'])
@permission_classes([IsSchoolAdmin])
def admin_student_analytics(request):
    students = CustomUser.objects.filter(role='learner').order_by('grade_level', 'last_name', 'first_name')
    data = []
    for student in students:
        attempts = QuizAttempt.objects.filter(user=student).select_related('quiz', 'quiz__learning_area')
        student_attempts = []
        total_score = 0
        total_possible = 0
        for attempt in attempts:
            percentage = round(attempt.score / attempt.total * 100) if attempt.total > 0 else 0
            student_attempts.append({
                "quiz_id": attempt.quiz.id,
                "quiz_title": attempt.quiz.title,
                "subject": attempt.quiz.learning_area.name if attempt.quiz.learning_area else "N/A",
                "score": attempt.score,
                "total": attempt.total,
                "percentage": percentage
            })
            total_score += attempt.score
            total_possible += attempt.total
        attempt_count = attempts.count()
        avg_percentage = round(total_score / total_possible * 100, 2) if total_possible > 0 else 0
        data.append({
            "id": student.id,
            "name": student.get_full_name() or student.email,
            "username": student.username,
            "grade": student.grade_level or "N/A",
            "stream": student.class_stream.stream_name if student.class_stream and hasattr(student.class_stream, 'stream_name') else "N/A",
            "attempts": student_attempts,
            "total_attempts": attempt_count,
            "total_score": total_score,
            "total_possible": total_possible,
            "avg_percentage": avg_percentage
        })
    return Response(data)