# academics/views_rc_frontend.py
"""
Front-end views for the report card system.

Pages:
  1. Report Card Dashboard  — /academics/report-cards/
  2. Mark Entry Grid        — /academics/report-cards/class/<id>/term/<id>/marks/
  3. Save Marks (AJAX POST) — /academics/report-cards/class/<id>/term/<id>/save-marks/
  4. Report Card Detail     — /academics/report-cards/<pk>/
  5. Initialise Cards       — /academics/report-cards/class/<id>/term/<id>/init/
  6. Compute & Finalise     — /academics/report-cards/<pk>/compute/
"""

import json
from collections import defaultdict

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST

from academics.models import SchoolClass
from academics.models_grades import (
    AcademicTerm, MonthlyMark, ReportCard, ReportSubject, TermSynthesis,
)
from academics.report_card_generator import (
    _resolve_report_type, generate_bulk_pdf, generate_report_card_pdf,
)
from students.models import Student


# ─────────────────────────────────────────────────────────────
# 1. DASHBOARD
# ─────────────────────────────────────────────────────────────

@login_required
def rc_dashboard(request):
    """
    Report Card Dashboard.
    Shows all classes with their report card status for the selected term.
    """
    terms = AcademicTerm.objects.order_by('-academic_year', 'term')
    current_term = AcademicTerm.objects.filter(is_current=True).first()

    # Term selector
    selected_term_id = request.GET.get('term')
    if selected_term_id:
        selected_term = get_object_or_404(AcademicTerm, pk=selected_term_id)
    else:
        selected_term = current_term

    # Get all classes the user can see
    user = request.user
    if hasattr(user, 'staff_profile'):
        role = user.staff_profile.role
        if role in ('teacher', 'class_teacher'):
            # Teachers only see their own class
            classes = SchoolClass.objects.filter(
                class_teacher=user.staff_profile
            )
        else:
            classes = SchoolClass.objects.all().order_by('level', 'section')
    else:
        classes = SchoolClass.objects.all().order_by('level', 'section')

    # Build per-class status summary
    class_data = []
    for cls in classes:
        active_students = Student.objects.filter(
            current_class=cls, status='active'
        ).count()

        if selected_term:
            cards = ReportCard.objects.filter(
                school_class=cls, term=selected_term
            )
            cards_created = cards.count()
            cards_with_pdf = cards.exclude(pdf_file='').count()
            cards_finalised = cards.filter(is_finalised=True).count()
            # Count students who have at least one monthly mark
            students_with_marks = MonthlyMark.objects.filter(
                term=selected_term,
                student__current_class=cls
            ).values('student').distinct().count()
        else:
            cards_created = cards_with_pdf = cards_finalised = students_with_marks = 0

        rtype = _resolve_report_type(cls)
        class_data.append({
            'cls': cls,
            'active_students': active_students,
            'cards_created': cards_created,
            'cards_finalised': cards_finalised,
            'cards_with_pdf': cards_with_pdf,
            'students_with_marks': students_with_marks,
            'report_type': rtype,
            'report_type_display': dict(ReportCard.REPORT_TYPE_CHOICES).get(rtype, rtype),
            'progress_pct': round(students_with_marks / active_students * 100) if active_students else 0,
        })

    context = {
        'terms': terms,
        'selected_term': selected_term,
        'current_term': current_term,
        'class_data': class_data,
        'total_classes': len(class_data),
        'total_students': sum(d['active_students'] for d in class_data),
        'total_cards': sum(d['cards_created'] for d in class_data),
        'total_pdfs': sum(d['cards_with_pdf'] for d in class_data),
    }
    return render(request, 'report_cards/rc_dashboard.html', context)


# ─────────────────────────────────────────────────────────────
# 2. INITIALISE REPORT CARDS FOR A CLASS
# ─────────────────────────────────────────────────────────────

@login_required
@require_POST
def rc_init_class(request, class_id, term_id):
    """
    Create ReportCard records for all active students in a class/term.
    Safe to call multiple times (uses get_or_create).
    """
    cls  = get_object_or_404(SchoolClass, pk=class_id)
    term = get_object_or_404(AcademicTerm, pk=term_id)
    rtype = _resolve_report_type(cls)

    students = Student.objects.filter(current_class=cls, status='active')
    created = 0
    for student in students:
        _, was_created = ReportCard.objects.get_or_create(
            student=student,
            term=term,
            defaults={'school_class': cls, 'report_type': rtype}
        )
        if was_created:
            created += 1

    messages.success(
        request,
        f'✅ Initialised {created} new report card(s) for {cls} — Term {term.term}.'
        if created else
        f'Report cards already exist for all {students.count()} students in {cls}.'
    )
    return redirect('rc-marks', class_id=class_id, term_id=term_id)


# ─────────────────────────────────────────────────────────────
# 3. MARK ENTRY GRID
# ─────────────────────────────────────────────────────────────

@login_required
def rc_marks(request, class_id, term_id):
    """
    Mark entry grid — one page per class per term.
    Shows: all active students (rows) × all subjects (columns) × 3 months.
    Teachers enter scores directly into the table; saved via AJAX.
    """
    cls  = get_object_or_404(SchoolClass, pk=class_id)
    term = get_object_or_404(AcademicTerm, pk=term_id)
    rtype = _resolve_report_type(cls)

    students = Student.objects.filter(
        current_class=cls, status='active'
    ).order_by('last_name', 'first_name')

    subjects = ReportSubject.objects.filter(
        report_type=rtype
    ).order_by('order')

    # Fetch all existing marks for this class/term in one query
    existing_marks = MonthlyMark.objects.filter(
        term=term,
        student__in=students,
        subject__in=subjects,
    ).select_related('student', 'subject')

    # Build lookup: {student_id: {subject_id: {month: score}}}
    mark_lookup = defaultdict(lambda: defaultdict(dict))
    for m in existing_marks:
        mark_lookup[m.student_id][m.subject_id][m.month_number] = m.score

    # Month numbers for this term
    month_nums = term.month_range  # e.g. [1,2,3] for term 1

    # Month labels
    LABELS_EN = {1:'1st Month',2:'2nd Month',3:'3rd Month',
                 4:'4th Month',5:'5th Month',6:'6th Month',
                 7:'7th Month',8:'8th Month',9:'9th Month'}
    LABELS_FR = {1:'1er Mois',2:'2e Mois',3:'3e Mois',
                 4:'4e Mois',5:'5e Mois',6:'6e Mois',
                 7:'7e Mois',8:'8e Mois',9:'9e Mois'}
    labels = LABELS_FR if rtype in ('fp','fn') else LABELS_EN

    month_labels = [labels[mn] for mn in month_nums]

    # Build subject groups for display (domain headers)
    subject_groups = []
    current_domain = None
    for subj in subjects:
        if subj.domain != current_domain:
            subject_groups.append({'domain': subj.domain, 'subjects': []})
            current_domain = subj.domain
        subject_groups[-1]['subjects'].append(subj)

    # Build per-student data
    student_rows = []
    for student in students:
        subj_cells = []
        for subj in subjects:
            scores = []
            for mn in month_nums:
                score = mark_lookup[student.id][subj.id].get(mn)
                scores.append({
                    'month': mn,
                    'score': float(score) if score is not None else '',
                    'input_id': f's{student.id}_sub{subj.id}_m{mn}',
                })
            subj_cells.append({'subject': subj, 'scores': scores})
        student_rows.append({'student': student, 'cells': subj_cells})

    # Check if report cards exist
    rc_count = ReportCard.objects.filter(school_class=cls, term=term).count()

    context = {
        'cls': cls,
        'term': term,
        'rtype': rtype,
        'subjects': subjects,
        'subject_groups': subject_groups,
        'month_nums': month_nums,
        'month_labels': month_labels,
        'student_rows': student_rows,
        'rc_initialised': rc_count > 0,
        'rc_count': rc_count,
        'student_count': students.count(),
        'all_terms': AcademicTerm.objects.order_by('-academic_year', 'term'),
    }
    return render(request, 'report_cards/rc_marks.html', context)


# ─────────────────────────────────────────────────────────────
# 4. SAVE MARKS (AJAX)
# ─────────────────────────────────────────────────────────────

@login_required
@require_POST
def rc_save_marks(request, class_id, term_id):
    """
    AJAX endpoint — save monthly marks for one student.
    POST body (JSON):
    {
        "student_id": 123,
        "marks": [
            {"subject_id": 5, "month": 1, "score": 18.5},
            {"subject_id": 5, "month": 2, "score": 16.0},
            ...
        ]
    }
    Returns: {"saved": N, "errors": [...]}
    """
    cls  = get_object_or_404(SchoolClass, pk=class_id)
    term = get_object_or_404(AcademicTerm, pk=term_id)

    try:
        data = json.loads(request.body)
        student_id = int(data['student_id'])
        marks_data = data['marks']
    except (KeyError, ValueError, json.JSONDecodeError) as e:
        return JsonResponse({'error': str(e)}, status=400)

    student = get_object_or_404(Student, pk=student_id)
    saved = 0
    errors = []

    # Get entering staff
    entered_by = None
    if hasattr(request.user, 'staff_profile'):
        entered_by = request.user.staff_profile

    for item in marks_data:
        try:
            subject = ReportSubject.objects.get(pk=item['subject_id'])
            month   = int(item['month'])
            score_raw = item.get('score')

            if score_raw == '' or score_raw is None:
                # Delete mark if cleared
                MonthlyMark.objects.filter(
                    student=student, term=term,
                    subject=subject, month_number=month
                ).delete()
            else:
                score = float(score_raw)
                if not (0 <= score <= 20):
                    errors.append(f'{subject.sub_activity} M{month}: score must be 0–20')
                    continue
                MonthlyMark.objects.update_or_create(
                    student=student, term=term,
                    subject=subject, month_number=month,
                    defaults={'score': score, 'entered_by': entered_by}
                )
                saved += 1
        except Exception as e:
            errors.append(str(e))

    return JsonResponse({'saved': saved, 'errors': errors})


# ─────────────────────────────────────────────────────────────
# 5. COMPUTE SYNTHESES FOR ONE REPORT CARD
# ─────────────────────────────────────────────────────────────

@login_required
@require_POST
def rc_compute(request, pk):
    """
    Compute synthese scores and class positions for one report card,
    then redirect back to the mark entry page.
    """
    rc = get_object_or_404(ReportCard, pk=pk)

    # Compute per-subject syntheses
    subjects = ReportSubject.objects.filter(report_type=rc.report_type)
    for subj in subjects:
        ts, _ = TermSynthesis.objects.get_or_create(
            student=rc.student, term=rc.term, subject=subj
        )
        ts.compute_from_monthly()

    # Compute overall totals
    rc.compute_totals()
    rc.compute_class_positions()

    messages.success(
        request,
        f'✅ Computed: {rc.student.get_full_name()} — '
        f'Average {rc.average}/20, Position {rc.position}.'
    )
    return redirect(
        'rc-marks',
        class_id=rc.school_class_id,
        term_id=rc.term_id
    )


# ─────────────────────────────────────────────────────────────
# 6. COMPUTE ALL FOR A CLASS (AJAX)
# ─────────────────────────────────────────────────────────────

@login_required
@require_POST
def rc_compute_class(request, class_id, term_id):
    """
    Compute syntheses for ALL students in a class in one shot.
    Called from the mark entry page via a button.
    """
    cls  = get_object_or_404(SchoolClass, pk=class_id)
    term = get_object_or_404(AcademicTerm, pk=term_id)

    cards = ReportCard.objects.filter(school_class=cls, term=term)
    if not cards.exists():
        messages.warning(request, 'No report cards found. Please initialise first.')
        return redirect('rc-marks', class_id=class_id, term_id=term_id)

    computed = 0
    for rc in cards:
        subjects = ReportSubject.objects.filter(report_type=rc.report_type)
        for subj in subjects:
            ts, _ = TermSynthesis.objects.get_or_create(
                student=rc.student, term=rc.term, subject=subj
            )
            ts.compute_from_monthly()
        rc.compute_totals()
        computed += 1

    # Rank all at once
    cards.first().compute_class_positions() if cards.exists() else None

    messages.success(
        request,
        f'✅ Computed averages and positions for {computed} students in {cls}.'
    )
    return redirect('rc-marks', class_id=class_id, term_id=term_id)


# ─────────────────────────────────────────────────────────────
# 7. REPORT CARD DETAIL (per student — with comments/conduct)
# ─────────────────────────────────────────────────────────────

@login_required
def rc_detail(request, pk):
    """
    Single student report card detail — view marks, enter comments,
    set conduct and promotion decision, then generate PDF.
    """
    rc = get_object_or_404(ReportCard, pk=pk)

    if request.method == 'POST':
        # Save comments and conduct
        rc.conduct              = request.POST.get('conduct', '')
        rc.class_teacher_remark = request.POST.get('class_teacher_remark', '')
        rc.head_teacher_remark  = request.POST.get('head_teacher_remark', '')
        rc.promotion_status     = request.POST.get('promotion_status', 'pending')
        rc.promoted_to_class    = request.POST.get('promoted_to_class', '')
        rc.next_term_begins     = request.POST.get('next_term_begins') or None
        # Francophone conduct bools
        rc.discipline_brutal = 'discipline_brutal' in request.POST
        rc.absenteisme       = 'absenteisme' in request.POST
        rc.barvade           = 'barvade' in request.POST
        # Attendance
        try:
            rc.days_present = int(request.POST.get('days_present', 0))
            rc.days_absent  = int(request.POST.get('days_absent', 0))
            rc.days_late    = int(request.POST.get('days_late', 0))
        except ValueError:
            pass
        # Annual results (Term 3 only)
        if rc.term.term == 3:
            try:
                rc.term1_average   = request.POST.get('term1_average') or None
                rc.term2_average   = request.POST.get('term2_average') or None
                rc.term3_average   = request.POST.get('term3_average') or None
                rc.annual_average  = request.POST.get('annual_average') or None
                rc.annual_position = request.POST.get('annual_position') or None
            except Exception:
                pass

        rc.save()
        messages.success(request, '✅ Report card updated successfully.')
        return redirect('rc-detail', pk=pk)

    # Build subject/marks summary for display
    from academics.report_card_generator import _build_term_data
    term_data = _build_term_data(rc.student, rc.term, rc.report_type)

    # Sibling cards (other students in same class)
    sibling_cards = ReportCard.objects.filter(
        school_class=rc.school_class, term=rc.term
    ).exclude(pk=pk).order_by('student__last_name')[:5]

    context = {
        'rc': rc,
        'student': rc.student,
        'term': rc.term,
        'term_data': term_data,
        'sibling_cards': sibling_cards,
        'is_francophone': rc.report_type in ('fp', 'fn'),
        'is_term3': rc.term.term == 3,
    }
    return render(request, 'report_cards/rc_detail.html', context)
