from __future__ import annotations

from datetime import date, timedelta

from sqlalchemy.orm import Session

from ..config import settings
from ..models import Article112Eligibility, Evaluation, EvaluationWorkflow, Grade, Student, StudentEnrollment
from .workflow import institution_now


def add_business_days(start: date, days: int) -> date:
    current = start
    added = 0
    while added < days:
        current += timedelta(days=1)
        if current.weekday() < 5:
            added += 1
    return current


def calculate_article112(evaluation: Evaluation, db: Session) -> dict:
    if evaluation.school_year_id:
        students = (
            db.query(Student)
            .join(StudentEnrollment, StudentEnrollment.student_id == Student.id)
            .filter(
                StudentEnrollment.school_year_id == evaluation.school_year_id,
                StudentEnrollment.section_id == evaluation.section_id,
            )
            .all()
        )
    else:
        students = (
            db.query(Student)
            .filter(Student.section_id == evaluation.section_id, Student.is_active.is_(True))
            .all()
        )
    grades = {g.student_id: g for g in evaluation.grades}
    failed_ids: list[int] = []
    incomplete_ids: list[int] = []

    for student in students:
        grade = grades.get(student.id)
        if grade is None or (grade.score is None and not grade.absent):
            incomplete_ids.append(student.id)
            continue
        if grade.absent:
            if settings.article112_absence_counts_as_fail:
                failed_ids.append(student.id)
            continue
        if grade.score is not None and grade.score < evaluation.passing_score:
            failed_ids.append(student.id)

    denominator = len(students)
    percentage = (len(failed_ids) / denominator) if denominator else 0.0
    return {
        "total": denominator,
        "failed": len(failed_ids),
        "percentage": percentage,
        "triggered": percentage >= settings.article112_threshold,
        "failed_ids": failed_ids,
        "incomplete_ids": incomplete_ids,
    }


def create_article112_evaluation(parent: Evaluation, db: Session) -> Evaluation:
    existing = (
        db.query(Evaluation)
        .filter(Evaluation.parent_evaluation_id == parent.id, Evaluation.is_article112.is_(True))
        .first()
    )
    if existing:
        return existing

    published_date = parent.published_at.date() if parent.published_at else date.today()
    second = Evaluation(
        section_id=parent.section_id,
        subject_id=parent.subject_id,
        teacher_id=parent.teacher_id,
        school_year_id=parent.school_year_id,
        period=parent.period,
        evaluation_number=parent.evaluation_number,
        title=f"Artículo 112 - {parent.title}",
        technique=parent.technique,
        instrument=parent.instrument,
        content=parent.content,
        competencies=parent.competencies,
        evaluation_date=add_business_days(published_date, 3),
        max_score=parent.max_score,
        passing_score=parent.passing_score,
        status="draft",
        is_article112=True,
        parent_evaluation_id=parent.id,
        article112_deadline=add_business_days(published_date, 3),
    )
    db.add(second)
    db.flush()
    now = institution_now()
    db.add(EvaluationWorkflow(evaluation_id=second.id, configured_at=now, config_locked_at=now))

    result = calculate_article112(parent, db)
    grade_map = {g.student_id: g for g in parent.grades}
    for student_id in result["failed_ids"]:
        original = grade_map.get(student_id)
        db.add(
            Article112Eligibility(
                evaluation_id=second.id,
                student_id=student_id,
                original_score=original.score if original else None,
            )
        )
    return second


def definitive_score(parent: Evaluation, student_id: int, db: Session) -> float | None:
    original = (
        db.query(Grade)
        .filter(Grade.evaluation_id == parent.id, Grade.student_id == student_id)
        .first()
    )
    if not original:
        return None
    second = (
        db.query(Evaluation)
        .filter(Evaluation.parent_evaluation_id == parent.id, Evaluation.is_article112.is_(True))
        .first()
    )
    if second and second.status == "published":
        second_grade = (
            db.query(Grade)
            .filter(Grade.evaluation_id == second.id, Grade.student_id == student_id)
            .first()
        )
        if second_grade and second_grade.score is not None:
            return second_grade.score
    return original.score
