import os
import mimetypes

from django.http import FileResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated

from people.models import Parent
from academics.models import StudentEnrollment, ClassSubject, StudentSubject
from teacher.models import StudyMaterial, PreviousYearPaper


def _verify_student_access(request, student_id):
    """
    Return (parent, student, enrollment) if the logged-in user is a parent
    with access to student_id, else return (None, error_response).
    """
    try:
        parent = Parent.objects.get(external_user_id=request.user.id)
    except Parent.DoesNotExist:
        return None, None, None, Response(
            {"success": False, "message": "Parent profile not found"},
            status=status.HTTP_404_NOT_FOUND,
        )

    student_parent = (
        parent.student_parents.filter(student_id=student_id, is_active=True)
        .select_related("student")
        .first()
    )
    if not student_parent:
        return None, None, None, Response(
            {"success": False, "message": "Student not found or access denied"},
            status=status.HTTP_403_FORBIDDEN,
        )

    student = student_parent.student

    enrollment = (
        StudentEnrollment.objects.filter(student=student, is_active=True)
        .select_related(
            "academic_class",
            "academic_class__standard",
            "academic_class__section",
            "academic_class__academic_year",
        )
        .first()
    )
    if not enrollment:
        return parent, student, None, Response(
            {"success": False, "message": "No active enrollment found"},
            status=status.HTTP_404_NOT_FOUND,
        )

    return parent, student, enrollment, None


def _get_student_subject_ids(enrollment):
    """
    Return the set of subject IDs the student is allowed to see:
    - All mandatory subjects for the class
    - Elective/language only if the student specifically enrolled
    """
    class_subject_ids = set(
        ClassSubject.objects.filter(
            academic_class=enrollment.academic_class
        ).values_list("subject_id", flat=True)
    )
    student_specific_ids = set(
        StudentSubject.objects.filter(
            enrollment=enrollment, is_active=True
        ).values_list("subject_id", flat=True)
    )
    # mandatory subjects from class + student-specific electives/languages
    return class_subject_ids | student_specific_ids


# ─────────────────────────────────────────────────────────────
# STUDY MATERIALS (read-only for parent/student)
# ─────────────────────────────────────────────────────────────


class ParentStudyMaterialsView(APIView):
    """
    GET /api/parent/study-materials/?student_id=<id>
    GET /api/parent/study-materials/?student_id=<id>&subject_id=<id>
    GET /api/parent/study-materials/?student_id=<id>&material_type=NOTES

    Returns study materials uploaded by teachers for the student's subjects.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        student_id = request.query_params.get("student_id")
        if not student_id:
            return Response(
                {"success": False, "message": "student_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        parent, student, enrollment, err = _verify_student_access(request, student_id)
        if err:
            return err

        allowed_subject_ids = _get_student_subject_ids(enrollment)

        subject_id = request.query_params.get("subject_id")
        material_type = request.query_params.get("material_type")

        qs = StudyMaterial.objects.filter(
            is_active=True,
            subject_id__in=allowed_subject_ids,
        ).select_related("subject", "subject__standard", "uploaded_by")

        if subject_id:
            if int(subject_id) not in allowed_subject_ids:
                return Response(
                    {"success": False, "message": "Subject not accessible for this student"},
                    status=status.HTTP_403_FORBIDDEN,
                )
            qs = qs.filter(subject_id=subject_id)

        if material_type:
            qs = qs.filter(material_type=material_type)

        # show materials for this class OR materials with no class restriction
        class_id = request.query_params.get("class_id")
        if class_id:
            qs = qs.filter(academic_class_id=class_id)
        else:
            from django.db.models import Q
            qs = qs.filter(
                Q(academic_class__isnull=True) | Q(academic_class=enrollment.academic_class)
            )

        materials = []
        for m in qs.order_by("-created_at"):
            file_url = None
            file_name = None
            if m.file:
                file_url = request.build_absolute_uri(m.file.url)
                file_name = os.path.basename(m.file.name)
            materials.append({
                "id": m.id,
                "title": m.title,
                "description": m.description,
                "subject_id": m.subject_id,
                "subject_name": m.subject.name,
                "subject_code": m.subject.code,
                "standard_name": m.subject.standard.name if m.subject.standard else None,
                "material_type": m.material_type,
                "material_type_display": m.get_material_type_display(),
                "file_url": file_url,
                "file_name": file_name,
                "uploaded_by": (
                    f"{m.uploaded_by.first_name} {m.uploaded_by.last_name}".strip()
                    if m.uploaded_by else None
                ),
                "created_at": m.created_at.isoformat(),
            })

        # Group by subject for easy consumption
        subjects_map = {}
        for m in materials:
            sid = m["subject_id"]
            if sid not in subjects_map:
                subjects_map[sid] = {
                    "subject_id": sid,
                    "subject_name": m["subject_name"],
                    "subject_code": m["subject_code"],
                    "standard_name": m["standard_name"],
                    "materials": [],
                }
            subjects_map[sid]["materials"].append(m)

        return Response({
            "success": True,
            "student": {
                "id": student.id,
                "full_name": student.full_name or f"{student.first_name or ''} {student.last_name or ''}".strip(),
                "admission_number": student.admission_number or "",
            },
            "enrollment_id": enrollment.id,
            "class_id": enrollment.academic_class.id,
            "total_materials": len(materials),
            "by_subject": list(subjects_map.values()),
            "materials": materials,
        })


class ParentStudyMaterialDownloadView(APIView):
    """
    GET /api/parent/study-materials/<pk>/download/?student_id=<id>

    Streams the file. Verifies student has access to that subject.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request, pk):
        student_id = request.query_params.get("student_id")
        if not student_id:
            return Response({"success": False, "message": "student_id is required"}, status=400)

        parent, student, enrollment, err = _verify_student_access(request, student_id)
        if err:
            return err

        try:
            material = StudyMaterial.objects.select_related("subject").get(pk=pk, is_active=True)
        except StudyMaterial.DoesNotExist:
            return Response({"success": False, "message": "Material not found"}, status=404)

        allowed = _get_student_subject_ids(enrollment)
        if material.subject_id not in allowed:
            return Response({"success": False, "message": "Access denied"}, status=403)

        file_path = material.file.path
        if not os.path.exists(file_path):
            return Response({"success": False, "message": "File not found on server"}, status=404)

        mime_type, _ = mimetypes.guess_type(file_path)
        response = FileResponse(
            open(file_path, "rb"),
            content_type=mime_type or "application/octet-stream",
        )
        response["Content-Disposition"] = f'attachment; filename="{os.path.basename(file_path)}"'
        return response


# ─────────────────────────────────────────────────────────────
# PREVIOUS YEAR PAPERS (read-only for parent/student)
# ─────────────────────────────────────────────────────────────


class ParentPreviousYearPapersView(APIView):
    """
    GET /api/parent/previous-year-papers/?student_id=<id>
    GET /api/parent/previous-year-papers/?student_id=<id>&subject_id=<id>
    GET /api/parent/previous-year-papers/?student_id=<id>&year=2023
    GET /api/parent/previous-year-papers/?student_id=<id>&exam_type=ANNUAL

    Returns previous year question papers for the student's subjects/standard.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        student_id = request.query_params.get("student_id")
        if not student_id:
            return Response(
                {"success": False, "message": "student_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        parent, student, enrollment, err = _verify_student_access(request, student_id)
        if err:
            return err

        allowed_subject_ids = _get_student_subject_ids(enrollment)
        standard = enrollment.academic_class.standard

        subject_id = request.query_params.get("subject_id")
        year = request.query_params.get("year")
        exam_type = request.query_params.get("exam_type")

        # PYQs are for the student's standard — show all subjects in that standard
        # but only subjects the student is enrolled in
        qs = PreviousYearPaper.objects.filter(
            is_active=True,
            standard=standard,
            subject_id__in=allowed_subject_ids,
        ).select_related("subject", "standard", "uploaded_by")

        if subject_id:
            if int(subject_id) not in allowed_subject_ids:
                return Response(
                    {"success": False, "message": "Subject not accessible for this student"},
                    status=status.HTTP_403_FORBIDDEN,
                )
            qs = qs.filter(subject_id=subject_id)

        if year:
            qs = qs.filter(year=year)

        if exam_type:
            qs = qs.filter(exam_type=exam_type)

        papers = []
        for p in qs.order_by("-year", "-created_at"):
            file_url = None
            file_name = None
            if p.file:
                file_url = request.build_absolute_uri(p.file.url)
                file_name = os.path.basename(p.file.name)
            papers.append({
                "id": p.id,
                "title": p.title or f"{p.subject.name} — {p.get_exam_type_display()} {p.year}",
                "subject_id": p.subject_id,
                "subject_name": p.subject.name,
                "subject_code": p.subject.code,
                "standard_id": p.standard_id,
                "standard_name": p.standard.name,
                "year": p.year,
                "exam_type": p.exam_type,
                "exam_type_display": p.get_exam_type_display(),
                "file_url": file_url,
                "file_name": file_name,
                "uploaded_by": (
                    f"{p.uploaded_by.first_name} {p.uploaded_by.last_name}".strip()
                    if p.uploaded_by else None
                ),
                "created_at": p.created_at.isoformat(),
            })

        # Group by subject
        subjects_map = {}
        for p in papers:
            sid = p["subject_id"]
            if sid not in subjects_map:
                subjects_map[sid] = {
                    "subject_id": sid,
                    "subject_name": p["subject_name"],
                    "subject_code": p["subject_code"],
                    "standard_name": p["standard_name"],
                    "papers": [],
                }
            subjects_map[sid]["papers"].append(p)

        # Available years for filter dropdown
        available_years = sorted(
            set(p["year"] for p in papers),
            reverse=True,
        )

        return Response({
            "success": True,
            "student": {
                "id": student.id,
                "full_name": student.full_name or f"{student.first_name or ''} {student.last_name or ''}".strip(),
                "admission_number": student.admission_number or "",
            },
            "enrollment_id": enrollment.id,
            "standard": {
                "id": standard.id,
                "name": standard.name,
                "code": standard.code,
            },
            "total_papers": len(papers),
            "available_years": available_years,
            "by_subject": list(subjects_map.values()),
            "papers": papers,
        })


class ParentPreviousYearPaperDownloadView(APIView):
    """
    GET /api/parent/previous-year-papers/<pk>/download/?student_id=<id>
    """

    permission_classes = [IsAuthenticated]

    def get(self, request, pk):
        student_id = request.query_params.get("student_id")
        if not student_id:
            return Response({"success": False, "message": "student_id is required"}, status=400)

        parent, student, enrollment, err = _verify_student_access(request, student_id)
        if err:
            return err

        try:
            paper = PreviousYearPaper.objects.select_related("subject", "standard").get(
                pk=pk, is_active=True
            )
        except PreviousYearPaper.DoesNotExist:
            return Response({"success": False, "message": "Paper not found"}, status=404)

        allowed = _get_student_subject_ids(enrollment)
        if paper.subject_id not in allowed:
            return Response({"success": False, "message": "Access denied"}, status=403)

        # Also check standard matches
        if paper.standard != enrollment.academic_class.standard:
            return Response({"success": False, "message": "Access denied"}, status=403)

        file_path = paper.file.path
        if not os.path.exists(file_path):
            return Response({"success": False, "message": "File not found on server"}, status=404)

        mime_type, _ = mimetypes.guess_type(file_path)
        response = FileResponse(
            open(file_path, "rb"),
            content_type=mime_type or "application/octet-stream",
        )
        response["Content-Disposition"] = f'attachment; filename="{os.path.basename(file_path)}"'
        return response
