import logging

from django.db import models
from django.utils import timezone
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from academics.models import AcademicClass, AcademicYear, StudentEnrollment
from fee.models import (
    ClassFeeStructure,
    PaymentFeeComponent,
    StudentFeeAssignment,
    TransportFeePayment,
)
from people.models import Teacher
from transport.models import StudentTransport

logger = logging.getLogger(__name__)

# Fee components whose name/code matches these are treated as transport fees
TRANSPORT_KEYWORDS = ("transport", "bus", "van")


def _is_transport_component(fee_component):
    text = f"{fee_component.name} {fee_component.code}".lower()
    return any(k in text for k in TRANSPORT_KEYWORDS)


class TeacherClassFeeDuesView(APIView):
    """
    GET /teacher/fee-dues/?page=1&page_size=10

    For the class where the requesting teacher is class teacher, returns
    students that have pending fees — with a component-wise breakdown (which
    components are unpaid/partial/overdue), bus/transport fee status, and the
    parent contact needed to send a chat reminder. Paginated (summary covers
    the whole class regardless of page).
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        try:
            try:
                page = max(1, int(request.query_params.get("page", 1)))
            except (TypeError, ValueError):
                page = 1
            try:
                page_size = min(50, max(1, int(request.query_params.get("page_size", 10))))
            except (TypeError, ValueError):
                page_size = 10

            teacher = Teacher.objects.get(external_user_id=request.user.id)

            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_404_NOT_FOUND,
                )

            academic_class = (
                AcademicClass.objects.filter(
                    class_teacher=teacher,
                    academic_year=academic_year,
                    is_active=True,
                )
                .select_related("standard", "section", "academic_year")
                .first()
            )
            if not academic_class:
                return Response(
                    {
                        "success": False,
                        "message": "You are not assigned as class teacher for any class in the current academic year",
                    },
                    status=status.HTTP_404_NOT_FOUND,
                )

            class_name = f"{academic_class.standard.name} - {academic_class.section.code}"
            active_terms = list(academic_year.terms.filter(is_active=True))

            # Class fee structures per term, fetched once
            class_fees = list(
                ClassFeeStructure.objects.filter(
                    academic_class=academic_class,
                    academic_term__in=active_terms,
                    is_active=True,
                ).select_related("fee_component", "academic_term")
            )
            transport_tracked_in_fees = any(
                _is_transport_component(cf.fee_component) for cf in class_fees
            )

            enrollments = (
                StudentEnrollment.objects.filter(
                    academic_class=academic_class, is_active=True
                )
                .select_related("student")
                .order_by("roll_number")
            )

            students_with_dues = []
            total_class_pending = 0.0
            overdue_students = 0

            for enrollment in enrollments:
                entry = self._build_student_dues(
                    enrollment, class_fees, transport_tracked_in_fees
                )
                if entry["total_pending"] <= 0:
                    continue
                students_with_dues.append(entry)
                total_class_pending += entry["total_pending"]
                if entry["has_overdue"]:
                    overdue_students += 1

            # Most urgent (largest pending) first
            students_with_dues.sort(key=lambda s: s["total_pending"], reverse=True)

            total_items = len(students_with_dues)
            total_pages = max(1, -(-total_items // page_size))  # ceil
            start = (page - 1) * page_size
            page_students = students_with_dues[start : start + page_size]

            return Response(
                {
                    "success": True,
                    "data": {
                        "class_info": {
                            "id": academic_class.id,
                            "name": class_name,
                            "academic_year": academic_year.name,
                        },
                        "summary": {
                            "total_students": enrollments.count(),
                            "students_with_dues": total_items,
                            "students_overdue": overdue_students,
                            "total_pending_amount": round(total_class_pending, 2),
                        },
                        "students": page_students,
                        "pagination": {
                            "current_page": page,
                            "page_size": page_size,
                            "total_pages": total_pages,
                            "total_items": total_items,
                            "has_next": page < total_pages,
                        },
                    },
                },
                status=status.HTTP_200_OK,
            )

        except Teacher.DoesNotExist:
            return Response(
                {"success": False, "message": "Teacher profile not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            logger.error(f"Error in TeacherClassFeeDuesView: {e}", exc_info=True)
            return Response(
                {"success": False, "message": f"An error occurred: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    # ── Per-student computation ──────────────────────────────────────────────

    def _build_student_dues(self, enrollment, class_fees, transport_tracked_in_fees):
        student = enrollment.student
        today = timezone.now().date()

        pending_components = []
        total_fees = 0.0
        total_paid = 0.0
        has_overdue = False

        for class_fee in class_fees:
            assignment = StudentFeeAssignment.objects.filter(
                enrollment=enrollment, class_fee_structure=class_fee, is_active=True
            ).first()

            if assignment:
                total_amount = float(assignment.calculate_final_amount())
                due_date = assignment.due_date_override or class_fee.due_date
            else:
                total_amount = float(class_fee.amount)
                due_date = class_fee.due_date

            paid_amount = self._get_paid_amount(enrollment, class_fee, assignment)
            pending = max(0.0, total_amount - paid_amount)

            total_fees += total_amount
            total_paid += paid_amount

            if pending <= 0:
                continue

            is_overdue = bool(due_date and due_date < today)
            has_overdue = has_overdue or is_overdue

            pending_components.append(
                {
                    "component_id": class_fee.fee_component.id,
                    "component_name": class_fee.fee_component.name,
                    "term_name": class_fee.academic_term.name,
                    "total_amount": round(total_amount, 2),
                    "paid_amount": round(paid_amount, 2),
                    "pending_amount": round(pending, 2),
                    "due_date": due_date.strftime("%d %b %Y") if due_date else None,
                    "is_overdue": is_overdue,
                    "is_transport": _is_transport_component(class_fee.fee_component),
                }
            )

        transport = self._get_transport_dues(enrollment, transport_tracked_in_fees)

        component_pending = sum(c["pending_amount"] for c in pending_components)
        total_pending = component_pending + transport["untracked_pending"]

        return {
            "student_id": student.id,
            "enrollment_id": enrollment.id,
            "student_name": student.full_name
            or f"{student.first_name} {student.last_name}".strip(),
            "roll_number": enrollment.roll_number,
            "profile_image": self._profile_image_url(student),
            "parents": self._get_parents(student),
            "pending_components": pending_components,
            "transport": transport["info"],
            "total_fees": round(total_fees, 2),
            "total_paid": round(total_paid, 2),
            "total_pending": round(total_pending, 2),
            "has_overdue": has_overdue,
        }

    def _get_paid_amount(self, enrollment, class_fee, assignment):
        """Same logic as parent StudentFeeDetailsView."""
        try:
            if assignment:
                paid = (
                    PaymentFeeComponent.objects.filter(
                        student_fee_assignment=assignment
                    ).aggregate(total=models.Sum("amount_paid"))["total"]
                    or 0
                )
            else:
                paid = (
                    PaymentFeeComponent.objects.filter(
                        class_fee_structure=class_fee,
                        payment__student_enrollment=enrollment,
                    ).aggregate(total=models.Sum("amount_paid"))["total"]
                    or 0
                )
            return float(paid)
        except Exception:
            return 0.0

    def _get_transport_dues(self, enrollment, transport_tracked_in_fees):
        """
        Bus fee status. If a transport-named fee component exists in the class
        fee structure, payments are tracked there (components carry
        is_transport=True) and we only report assignment info. Otherwise
        StudentTransport.fee_amount is tracked via TransportFeePayment, so we
        net out what's already been paid to get the real pending amount.
        """
        st = (
            StudentTransport.objects.filter(enrollment=enrollment, is_active=True)
            .select_related("vehicle", "route")
            .first()
        )
        if not st:
            return {
                "info": {"has_transport": False},
                "untracked_pending": 0.0,
            }

        fee_amount = float(st.fee_amount or 0)
        if transport_tracked_in_fees:
            untracked_pending = 0.0
        else:
            paid = float(
                TransportFeePayment.objects.filter(student_transport=st).aggregate(
                    total=models.Sum("amount_paid")
                )["total"]
                or 0
            )
            untracked_pending = max(0.0, fee_amount - paid)

        return {
            "info": {
                "has_transport": True,
                "bus_number": st.vehicle.bus_number if st.vehicle else None,
                "route_name": st.route.route_name if st.route else None,
                "fee_amount": round(fee_amount, 2),
                "tracked_in_fee_components": transport_tracked_in_fees,
                "pending_amount": round(untracked_pending, 2),
                "due_date": st.due_date,
                "due_date_formatted": (
                    st.due_date.strftime("%d %b %Y") if st.due_date else None
                ),
            },
            "untracked_pending": untracked_pending,
        }

    def _get_parents(self, student):
        parents = []
        for sp in student.student_parents.filter(is_active=True).select_related(
            "parent"
        ):
            p = sp.parent
            parents.append(
                {
                    "id": p.id,
                    "name": p.full_name
                    or f"{p.first_name or ''} {p.last_name or ''}".strip(),
                    "relationship": sp.relationship,
                    "phone": p.phone,
                }
            )
        return parents

    def _profile_image_url(self, student):
        try:
            img = getattr(student, "profile_image", None)
            return img.url if img else None
        except Exception:
            return None
