from __future__ import annotations

from collections import defaultdict
from statistics import mean

from sqlalchemy.orm import Session, joinedload

from ..models import Evaluation, Grade, SchoolYear, Section, Student, Subject
from .academic import students_for_section_year
from .article112 import definitive_score


ANNUAL_PERIODS = ("I Momento", "II Momento", "III Momento")


def _rounded_average(values: list[float | None]) -> float | None:
    clean = [float(value) for value in values if value is not None]
    return round(mean(clean), 2) if clean else None


def build_grade_report(
    db: Session,
    *,
    school_year: SchoolYear,
    section: Section,
    subject: Subject,
    scope: str,
    period: str | None = None,
) -> dict:
    if scope not in {"period", "annual"}:
        raise ValueError("Tipo de reporte inválido")
    if scope == "period" and not period:
        raise ValueError("Debe seleccionar un lapso")

    evaluations_query = (
        db.query(Evaluation)
        .options(joinedload(Evaluation.grades))
        .filter(
            Evaluation.school_year_id == school_year.id,
            Evaluation.section_id == section.id,
            Evaluation.subject_id == subject.id,
            Evaluation.status == "published",
            Evaluation.is_article112.is_(False),
        )
    )
    if scope == "period":
        evaluations_query = evaluations_query.filter(Evaluation.period == period)
    else:
        evaluations_query = evaluations_query.filter(Evaluation.period.in_(ANNUAL_PERIODS))
    evaluations = evaluations_query.order_by(Evaluation.evaluation_date, Evaluation.evaluation_number, Evaluation.id).all()

    students = students_for_section_year(db, section.id, school_year.id, include_inactive=True)
    rows: list[dict] = []
    approved = 0
    failed = 0
    class_averages: list[float] = []

    if scope == "period":
        for student in students:
            scores = [definitive_score(evaluation, student.id, db) for evaluation in evaluations]
            average = _rounded_average(scores)
            status = "Sin notas" if average is None else ("Aprobado" if average >= 10 else "Reprobado")
            if average is not None:
                class_averages.append(average)
                approved += int(average >= 10)
                failed += int(average < 10)
            rows.append({"student": student, "scores": scores, "average": average, "status": status})
        columns = [
            {"id": evaluation.id, "label": f"E{evaluation.evaluation_number}", "title": evaluation.title}
            for evaluation in evaluations
        ]
    else:
        by_period: dict[str, list[Evaluation]] = defaultdict(list)
        for evaluation in evaluations:
            by_period[evaluation.period].append(evaluation)
        for student in students:
            period_averages = {
                period_name: _rounded_average(
                    [definitive_score(evaluation, student.id, db) for evaluation in by_period.get(period_name, [])]
                )
                for period_name in ANNUAL_PERIODS
            }
            final_average = _rounded_average(list(period_averages.values()))
            status = "Sin notas" if final_average is None else ("Aprobado" if final_average >= 10 else "Reprobado")
            if final_average is not None:
                class_averages.append(final_average)
                approved += int(final_average >= 10)
                failed += int(final_average < 10)
            rows.append(
                {
                    "student": student,
                    "period_averages": period_averages,
                    "average": final_average,
                    "status": status,
                }
            )
        columns = [{"id": name, "label": name, "title": f"Promedio {name}"} for name in ANNUAL_PERIODS]

    return {
        "school_year": school_year,
        "section": section,
        "subject": subject,
        "scope": scope,
        "period": period,
        "evaluations": evaluations,
        "columns": columns,
        "rows": rows,
        "summary": {
            "students": len(students),
            "with_grades": len(class_averages),
            "approved": approved,
            "failed": failed,
            "class_average": _rounded_average(class_averages),
        },
    }
