import os
import mimetypes

from django.http import FileResponse, Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated

from rest_framework.parsers import MultiPartParser, FormParser

from people.models import Teacher
from academics.models import Subject, Standard, AcademicClass, SubjectTeacher
from teacher.models import StudyMaterial, PreviousYearPaper
from teacher.serializers import StudyMaterialSerializer, PreviousYearPaperSerializer


def _get_teacher(request):
    """Return Teacher for the logged-in user (matches pattern used across all teacher views)."""
    try:
        return Teacher.objects.get(external_user_id=request.user.id, is_active=True)
    except Teacher.DoesNotExist:
        return None


def _taught_subject_ids(teacher):
    """Subject IDs the teacher is currently assigned to teach."""
    return list(
        SubjectTeacher.objects.filter(teacher=teacher, is_active=True)
        .values_list("subject_id", flat=True)
        .distinct()
    )


# ─────────────────────────────────────────────────────────────
# STUDY MATERIALS
# ─────────────────────────────────────────────────────────────


class StudyMaterialListCreateView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    def get(self, request):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)

        # Optional filters
        subject_id = request.query_params.get("subject_id")
        class_id = request.query_params.get("class_id")
        material_type = request.query_params.get("material_type")

        # Shared pool: any teacher assigned to a subject can see all materials
        # uploaded for that subject (by themselves or a colleague teaching it).
        taught_subject_ids = _taught_subject_ids(teacher)

        qs = StudyMaterial.objects.filter(
            subject_id__in=taught_subject_ids, is_active=True
        ).select_related("subject", "subject__standard", "academic_class", "uploaded_by")

        if subject_id:
            qs = qs.filter(subject_id=subject_id)
        if class_id:
            qs = qs.filter(academic_class_id=class_id)
        if material_type:
            qs = qs.filter(material_type=material_type)

        qs = qs.order_by("-created_at")

        serializer = StudyMaterialSerializer(qs, many=True, context={"request": request})
        return Response(serializer.data)

    def post(self, request):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)

        serializer = StudyMaterialSerializer(data=request.data, context={"request": request})
        if serializer.is_valid():
            subject = serializer.validated_data.get("subject")
            if subject and subject.id not in _taught_subject_ids(teacher):
                return Response(
                    {"error": "You can only upload materials for subjects you teach."},
                    status=status.HTTP_403_FORBIDDEN,
                )
            # Use objects.create directly so uploaded_by is always persisted,
            # regardless of read_only_fields behaviour in the serializer.
            instance = StudyMaterial.objects.create(
                uploaded_by=teacher,
                is_active=True,
                **{k: v for k, v in serializer.validated_data.items() if k != "is_active"},
            )
            out = StudyMaterialSerializer(instance, context={"request": request})
            return Response(out.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class StudyMaterialDetailView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    def _get_viewable(self, pk, teacher):
        """Any material for a subject this teacher teaches (shared pool)."""
        try:
            return StudyMaterial.objects.get(
                pk=pk, subject_id__in=_taught_subject_ids(teacher), is_active=True
            )
        except StudyMaterial.DoesNotExist:
            return None

    def _get_owned(self, pk, teacher):
        """Only materials this teacher uploaded (for edit/delete)."""
        try:
            return StudyMaterial.objects.get(pk=pk, uploaded_by=teacher, is_active=True)
        except StudyMaterial.DoesNotExist:
            return None

    def get(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_viewable(pk, teacher)
        if not obj:
            return Response({"error": "Not found."}, status=404)
        return Response(StudyMaterialSerializer(obj, context={"request": request}).data)

    def patch(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_owned(pk, teacher)
        if not obj:
            if self._get_viewable(pk, teacher):
                return Response(
                    {"error": "You can only edit your own materials."}, status=403
                )
            return Response({"error": "Not found."}, status=404)
        serializer = StudyMaterialSerializer(
            obj, data=request.data, partial=True, context={"request": request}
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_owned(pk, teacher)
        if not obj:
            if self._get_viewable(pk, teacher):
                return Response(
                    {"error": "You can only delete your own materials."}, status=403
                )
            return Response({"error": "Not found."}, status=404)
        # Soft-delete
        obj.is_active = False
        obj.save(update_fields=["is_active"])
        return Response(status=status.HTTP_204_NO_CONTENT)


class StudyMaterialDownloadView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, pk):
        try:
            obj = StudyMaterial.objects.get(pk=pk, is_active=True)
        except StudyMaterial.DoesNotExist:
            return Response({"error": "Not found."}, status=404)

        file_path = obj.file.path
        if not os.path.exists(file_path):
            return Response({"error": "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",
        )
        filename = os.path.basename(file_path)
        response["Content-Disposition"] = f'attachment; filename="{filename}"'
        return response


# ─────────────────────────────────────────────────────────────
# PREVIOUS YEAR PAPERS
# ─────────────────────────────────────────────────────────────


class PreviousYearPaperListCreateView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    def get(self, request):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)

        subject_id = request.query_params.get("subject_id")
        standard_id = request.query_params.get("standard_id")
        year = request.query_params.get("year")
        exam_type = request.query_params.get("exam_type")

        # Shared pool: any teacher assigned to a subject can see all papers
        # uploaded for that subject (by themselves or a colleague teaching it).
        taught_subject_ids = _taught_subject_ids(teacher)

        qs = PreviousYearPaper.objects.filter(
            subject_id__in=taught_subject_ids, is_active=True
        ).select_related("subject", "standard", "uploaded_by")

        if subject_id:
            qs = qs.filter(subject_id=subject_id)
        if standard_id:
            qs = qs.filter(standard_id=standard_id)
        if year:
            qs = qs.filter(year=year)
        if exam_type:
            qs = qs.filter(exam_type=exam_type)

        qs = qs.order_by("-created_at")

        serializer = PreviousYearPaperSerializer(qs, many=True, context={"request": request})
        return Response(serializer.data)

    def post(self, request):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)

        serializer = PreviousYearPaperSerializer(
            data=request.data, context={"request": request}
        )
        if serializer.is_valid():
            subject = serializer.validated_data.get("subject")
            if subject and subject.id not in _taught_subject_ids(teacher):
                return Response(
                    {"error": "You can only upload papers for subjects you teach."},
                    status=status.HTTP_403_FORBIDDEN,
                )
            instance = PreviousYearPaper.objects.create(
                uploaded_by=teacher,
                is_active=True,
                **{k: v for k, v in serializer.validated_data.items() if k != "is_active"},
            )
            out = PreviousYearPaperSerializer(instance, context={"request": request})
            return Response(out.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class PreviousYearPaperDetailView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    def _get_viewable(self, pk, teacher):
        """Any paper for a subject this teacher teaches (shared pool)."""
        try:
            return PreviousYearPaper.objects.get(
                pk=pk, subject_id__in=_taught_subject_ids(teacher), is_active=True
            )
        except PreviousYearPaper.DoesNotExist:
            return None

    def _get_owned(self, pk, teacher):
        """Only papers this teacher uploaded (for edit/delete)."""
        try:
            return PreviousYearPaper.objects.get(pk=pk, uploaded_by=teacher, is_active=True)
        except PreviousYearPaper.DoesNotExist:
            return None

    def get(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_viewable(pk, teacher)
        if not obj:
            return Response({"error": "Not found."}, status=404)
        return Response(PreviousYearPaperSerializer(obj, context={"request": request}).data)

    def patch(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_owned(pk, teacher)
        if not obj:
            if self._get_viewable(pk, teacher):
                return Response(
                    {"error": "You can only edit your own papers."}, status=403
                )
            return Response({"error": "Not found."}, status=404)
        serializer = PreviousYearPaperSerializer(
            obj, data=request.data, partial=True, context={"request": request}
        )
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)
        obj = self._get_owned(pk, teacher)
        if not obj:
            if self._get_viewable(pk, teacher):
                return Response(
                    {"error": "You can only delete your own papers."}, status=403
                )
            return Response({"error": "Not found."}, status=404)
        obj.is_active = False
        obj.save(update_fields=["is_active"])
        return Response(status=status.HTTP_204_NO_CONTENT)


class PreviousYearPaperDownloadView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, pk):
        try:
            obj = PreviousYearPaper.objects.get(pk=pk, is_active=True)
        except PreviousYearPaper.DoesNotExist:
            return Response({"error": "Not found."}, status=404)

        file_path = obj.file.path
        if not os.path.exists(file_path):
            return Response({"error": "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",
        )
        filename = os.path.basename(file_path)
        response["Content-Disposition"] = f'attachment; filename="{filename}"'
        return response


# ─────────────────────────────────────────────────────────────
# HELPER: subjects/standards available to the teacher
# ─────────────────────────────────────────────────────────────


class TeacherSubjectsForMaterialsView(APIView):
    """Return subjects + their standards that the teacher teaches."""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        teacher = _get_teacher(request)
        if not teacher:
            return Response({"error": "Teacher not found."}, status=404)

        rows = (
            SubjectTeacher.objects.filter(teacher=teacher, is_active=True)
            .select_related(
                "subject", "subject__standard", "academic_class",
                "academic_class__standard", "academic_class__section",
                "academic_class__academic_year",
            )
            .distinct()
        )

        subjects_map = {}
        for st in rows:
            sid = st.subject_id
            if sid not in subjects_map:
                subjects_map[sid] = {
                    "subject_id": st.subject.id,
                    "subject_name": st.subject.name,
                    "subject_code": st.subject.code,
                    "standard_id": st.subject.standard.id,
                    "standard_name": st.subject.standard.name,
                    "classes": [],
                }
            subjects_map[sid]["classes"].append({
                "class_id": st.academic_class.id,
                "class_name": str(st.academic_class),
                "is_active": st.academic_class.is_active,
            })

        return Response(list(subjects_map.values()))
