from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db import transaction
from django.db.models import Q, Avg, Count, Max, Min, F
from django.utils import timezone
from datetime import date as date_type, datetime

from people.models import Teacher
from academics.models import (
    AcademicClass,
    Subject,
    SubjectTeacher,
    AcademicYear,
    StudentEnrollment,
)
from schedules.models import TimeTable, WeekDay
from exam.models import ClassTest, ClassTestStudent

from .teacher_class_test_serializers import (
    AvailablePeriodSerializer,
    ClassTestCreateSerializer,
    ClassTestListSerializer,
    ClassTestDetailSerializer,
    ClassTestStudentSerializer,
    BulkMarksEntrySerializer,
)

import logging

logger = logging.getLogger(__name__)

# Weekday abbreviation map matching WeekDay.day_code values
WEEKDAY_MAP = {
    0: "MON",
    1: "TUE",
    2: "WED",
    3: "THU",
    4: "FRI",
    5: "SAT",
    6: "SUN",
}


def _get_teacher(request):
    return Teacher.objects.get(external_user_id=request.user.id)


class TeacherAvailablePeriodsView(APIView):
    """
    GET /teacher/class-tests/available-periods/?date=YYYY-MM-DD

    Returns the timetable periods assigned to the logged-in teacher on the
    given date (defaults to today).  Includes both subject-teacher slots and
    class-teacher ownership so the teacher knows which classes/subjects they
    can create a test for.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        date_str = request.query_params.get("date")
        try:
            query_date = (
                datetime.strptime(date_str, "%Y-%m-%d").date()
                if date_str
                else date_type.today()
            )
        except ValueError:
            return Response(
                {"success": False, "message": "Invalid date format. Use YYYY-MM-DD."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        day_code = WEEKDAY_MAP.get(query_date.weekday())
        day_names = {
            "MON": "Monday", "TUE": "Tuesday", "WED": "Wednesday",
            "THU": "Thursday", "FRI": "Friday", "SAT": "Saturday", "SUN": "Sunday",
        }
        day_name = day_names.get(day_code, day_code)

        # Resolve the WeekDay object — same as TeacherTodayScheduleView
        try:
            weekday = WeekDay.objects.get(day_code=day_code, is_active=True)
        except WeekDay.DoesNotExist:
            return Response({
                "success": True,
                "date": query_date.isoformat(),
                "day": day_code,
                "day_name": day_name,
                "message": f"No timetable configuration for {day_name}.",
                "subject_periods": [],
                "class_teacher_classes": [],
            })

        if not weekday.is_open:
            return Response({
                "success": True,
                "date": query_date.isoformat(),
                "day": day_code,
                "day_name": day_name,
                "message": f"School is closed on {day_name}.",
                "subject_periods": [],
                "class_teacher_classes": [],
            })

        # Query timetable exactly like TeacherTodayScheduleView:
        # — filter by weekday FK object (not day_code string)
        # — include substitute periods too
        # — exclude break slots
        timetables = (
            TimeTable.objects.filter(
                Q(subject_teacher__teacher=teacher) | Q(substitute_teacher=teacher),
                weekday=weekday,
                is_active=True,
                is_break=False,
            )
            .select_related(
                "subject",
                "subject_teacher",
                "academic_class",
                "academic_class__standard",
                "academic_class__section",
            )
            .order_by("period_number")
        )

        period_data = [
            {
                "id": tt.id,
                "academic_class": tt.academic_class_id,
                "class_name": str(tt.academic_class) if tt.academic_class else None,
                "subject_id": tt.subject_id,
                "subject_name": tt.subject.name if tt.subject else None,
                "subject_teacher_id": tt.subject_teacher_id,
                "period_number": tt.period_number,
                "start_time": tt.start_time.strftime("%H:%M:%S") if tt.start_time else None,
                "end_time": tt.end_time.strftime("%H:%M:%S") if tt.end_time else None,
                "room_number": tt.room_number,
                "is_substitution": tt.is_substitution,
                "is_class_teacher_slot": False,
            }
            for tt in timetables
        ]

        # Classes where this teacher is the class teacher (general test, no subject)
        class_teacher_classes = AcademicClass.objects.filter(
            class_teacher=teacher
        ).select_related("standard", "section")

        class_teacher_data = [
            {
                "id": None,
                "academic_class": cls.id,
                "class_name": str(cls),
                "subject_id": None,
                "subject_name": None,
                "subject_teacher_id": None,
                "period_number": None,
                "start_time": None,
                "end_time": None,
                "room_number": None,
                "is_class_teacher_slot": True,
            }
            for cls in class_teacher_classes
        ]

        return Response({
            "success": True,
            "date": query_date.isoformat(),
            "day": day_code,
            "day_name": day_name,
            "subject_periods": period_data,
            "class_teacher_classes": class_teacher_data,
        })


class ClassTestListCreateView(APIView):
    """
    GET  /teacher/class-tests/         — list tests created by this teacher
    POST /teacher/class-tests/         — create a new class test
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        qs = ClassTest.objects.filter(created_by=teacher).select_related(
            "academic_class", "subject", "academic_year"
        )

        # Optional filters
        test_status = request.query_params.get("status")
        if test_status:
            qs = qs.filter(status=test_status.upper())

        test_type = request.query_params.get("test_type")
        if test_type:
            qs = qs.filter(test_type=test_type.upper())

        class_id = request.query_params.get("academic_class")
        if class_id:
            qs = qs.filter(academic_class_id=class_id)

        from_date = request.query_params.get("from_date")
        to_date = request.query_params.get("to_date")
        if from_date:
            qs = qs.filter(test_date__gte=from_date)
        if to_date:
            qs = qs.filter(test_date__lte=to_date)

        serializer = ClassTestListSerializer(qs, many=True)
        return Response({"success": True, "results": serializer.data})

    @transaction.atomic
    def post(self, request):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        # Resolve academic year from request or use active one
        academic_year_id = request.data.get("academic_year")
        if academic_year_id:
            try:
                academic_year = AcademicYear.objects.get(id=academic_year_id)
            except AcademicYear.DoesNotExist:
                return Response(
                    {"success": False, "message": "Academic year not found."},
                    status=status.HTTP_404_NOT_FOUND,
                )
        else:
            academic_year = AcademicYear.objects.filter(is_active=True).first()
            if not academic_year:
                return Response(
                    {"success": False, "message": "No active academic year found."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        data = request.data.copy()
        data["academic_year"] = academic_year.id

        serializer = ClassTestCreateSerializer(
            data=data, context={"teacher": teacher}
        )
        if not serializer.is_valid():
            return Response(
                {"success": False, "errors": serializer.errors},
                status=status.HTTP_400_BAD_REQUEST,
            )

        class_test = serializer.save(created_by=teacher)

        # Auto-create student result rows for all active enrollments
        enrollments = StudentEnrollment.objects.filter(
            academic_class=class_test.academic_class, is_active=True
        )
        ClassTestStudent.objects.bulk_create(
            [
                ClassTestStudent(
                    class_test=class_test,
                    student_enrollment=enrollment,
                )
                for enrollment in enrollments
            ],
            ignore_conflicts=True,
        )

        return Response(
            {
                "success": True,
                "message": "Class test created successfully.",
                "data": ClassTestListSerializer(class_test).data,
            },
            status=status.HTTP_201_CREATED,
        )


class ClassTestDetailView(APIView):
    """
    GET    /teacher/class-tests/<id>/   — full test detail with student results
    PUT    /teacher/class-tests/<id>/   — edit (only when SCHEDULED)
    DELETE /teacher/class-tests/<id>/   — delete (only when SCHEDULED)
    """

    permission_classes = [IsAuthenticated]

    def _get_test(self, teacher, test_id):
        try:
            return ClassTest.objects.get(id=test_id, created_by=teacher)
        except ClassTest.DoesNotExist:
            return None

    def get(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        test = self._get_test(teacher, test_id)
        if not test:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        serializer = ClassTestDetailSerializer(test)
        return Response({"success": True, "data": serializer.data})

    @transaction.atomic
    def put(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        test = self._get_test(teacher, test_id)
        if not test:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        if test.status != "SCHEDULED":
            return Response(
                {"success": False, "message": "Only SCHEDULED tests can be edited."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        serializer = ClassTestCreateSerializer(
            test, data=request.data, partial=True, context={"teacher": teacher}
        )
        if not serializer.is_valid():
            return Response(
                {"success": False, "errors": serializer.errors},
                status=status.HTTP_400_BAD_REQUEST,
            )

        serializer.save()
        return Response(
            {
                "success": True,
                "message": "Class test updated successfully.",
                "data": ClassTestListSerializer(test).data,
            }
        )

    def delete(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        test = self._get_test(teacher, test_id)
        if not test:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        if test.status != "SCHEDULED":
            return Response(
                {
                    "success": False,
                    "message": "Only SCHEDULED tests can be deleted.",
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        test.delete()
        return Response(
            {"success": True, "message": "Class test deleted successfully."},
            status=status.HTTP_200_OK,
        )


class ClassTestStudentsView(APIView):
    """
    GET /teacher/class-tests/<id>/students/

    List all enrolled students for a class test with their current marks.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        try:
            test = ClassTest.objects.get(id=test_id, created_by=teacher)
        except ClassTest.DoesNotExist:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        student_results = (
            test.student_results.select_related(
                "student_enrollment__student",
            )
            .order_by("student_enrollment__roll_number")
        )

        serializer = ClassTestStudentSerializer(student_results, many=True)
        return Response(
            {
                "success": True,
                "test_id": test.id,
                "test_title": test.title,
                "max_marks": test.max_marks,
                "passing_marks": test.passing_marks,
                "status": test.status,
                "results": serializer.data,
            }
        )


class ClassTestBulkMarksView(APIView):
    """
    POST /teacher/class-tests/<id>/marks/

    Bulk enter or update marks for students in a class test.
    Body: { "marks": [ { "student_enrollment": <id>, "marks_obtained": <val>,
                          "is_absent": false, "remarks": "" }, ... ] }
    Auto-updates test status to MARKS_ENTERED when all students have marks.
    """

    permission_classes = [IsAuthenticated]

    @transaction.atomic
    def post(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        try:
            test = ClassTest.objects.get(id=test_id, created_by=teacher)
        except ClassTest.DoesNotExist:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        marks_data = request.data.get("marks", [])
        if not marks_data:
            return Response(
                {"success": False, "message": "No marks data provided."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        bulk_serializer = BulkMarksEntrySerializer(data=marks_data, many=True)
        if not bulk_serializer.is_valid():
            return Response(
                {"success": False, "errors": bulk_serializer.errors},
                status=status.HTTP_400_BAD_REQUEST,
            )

        validated = bulk_serializer.validated_data
        enrollment_ids = [item["student_enrollment"] for item in validated]

        # Validate all enrollment IDs belong to this test
        existing_records = {
            r.student_enrollment_id: r
            for r in ClassTestStudent.objects.filter(
                class_test=test, student_enrollment_id__in=enrollment_ids
            )
        }

        missing = set(enrollment_ids) - set(existing_records.keys())
        if missing:
            return Response(
                {
                    "success": False,
                    "message": f"Enrollment IDs not found in this test: {list(missing)}",
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Validate marks don't exceed max_marks
        now = timezone.now()
        for item in validated:
            mo = item.get("marks_obtained")
            if mo is not None and float(mo) > test.max_marks:
                return Response(
                    {
                        "success": False,
                        "message": f"Marks {mo} exceed max marks {test.max_marks}.",
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

        updated_count = 0
        for item in validated:
            record = existing_records[item["student_enrollment"]]
            record.is_absent = item["is_absent"]
            record.marks_obtained = None if item["is_absent"] else item.get("marks_obtained")
            record.remarks = item.get("remarks", "")
            record.marks_entered_by = teacher
            record.marks_entered_at = now
            record.save(
                update_fields=[
                    "is_absent",
                    "marks_obtained",
                    "remarks",
                    "marks_entered_by",
                    "marks_entered_at",
                ]
            )
            updated_count += 1

        # Auto-flip status to MARKS_ENTERED when all students are covered
        total = test.student_results.count()
        entered = test.student_results.filter(
            Q(marks_obtained__isnull=False) | Q(is_absent=True)
        ).count()
        if total > 0 and entered == total:
            test.status = "MARKS_ENTERED"
            test.save(update_fields=["status"])

        return Response(
            {
                "success": True,
                "message": f"Marks updated for {updated_count} student(s).",
                "test_status": test.status,
            }
        )


class ClassTestSummaryView(APIView):
    """
    GET /teacher/class-tests/<id>/summary/

    Returns aggregate statistics for a class test.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request, test_id):
        try:
            teacher = _get_teacher(request)
        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        try:
            test = ClassTest.objects.get(id=test_id, created_by=teacher)
        except ClassTest.DoesNotExist:
            return Response(
                {"success": False, "message": "Class test not found."},
                status=status.HTTP_404_NOT_FOUND,
            )

        results = test.student_results.all()
        total = results.count()
        absent = results.filter(is_absent=True).count()
        present = total - absent
        entered = results.filter(marks_obtained__isnull=False).count()

        agg = results.filter(marks_obtained__isnull=False).aggregate(
            avg_marks=Avg("marks_obtained"),
            highest=Max("marks_obtained"),
            lowest=Min("marks_obtained"),
        )

        passed = 0
        if test.passing_marks:
            passed = results.filter(
                is_absent=False,
                marks_obtained__gte=test.passing_marks,
            ).count()

        avg_marks = float(agg["avg_marks"]) if agg["avg_marks"] else None
        avg_percentage = (
            round(avg_marks / test.max_marks * 100, 2)
            if avg_marks and test.max_marks
            else None
        )

        return Response(
            {
                "success": True,
                "test_id": test.id,
                "title": test.title,
                "test_type": test.test_type,
                "test_date": test.test_date,
                "max_marks": test.max_marks,
                "passing_marks": test.passing_marks,
                "status": test.status,
                "stats": {
                    "total_students": total,
                    "absent": absent,
                    "present": present,
                    "marks_entered": entered,
                    "passed": passed if test.passing_marks else None,
                    "failed": (present - passed) if test.passing_marks else None,
                    "avg_marks": avg_marks,
                    "avg_percentage": avg_percentage,
                    "highest_marks": float(agg["highest"]) if agg["highest"] else None,
                    "lowest_marks": float(agg["lowest"]) if agg["lowest"] else None,
                },
            }
        )
