import math
from typing import List

import graphene
from django.utils import timezone

from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus


class AttendanceSummaryStatisticsType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    days_completed = graphene.Int(required=True)
    participants_present = graphene.Int(required=True)


class PresenceRecordStatisticsType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    course_session_id = graphene.ID(required=True)
    generation = graphene.String(required=True)
    circle_id = graphene.ID(required=True)
    due_date = graphene.DateTime(required=True)
    participants_present = graphene.Int(required=True)
    participants_total = graphene.Int(required=True)
    details_url = graphene.String(required=True)


class AttendanceDayPresencesStatisticsType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    records = graphene.List(
        graphene.NonNull(PresenceRecordStatisticsType), required=True
    )
    summary = graphene.Field(AttendanceSummaryStatisticsType, required=True)


def attendance_day_presences(
    course_id: graphene.ID,
    course_session_selection_ids: graphene.List(graphene.ID),
) -> AttendanceDayPresencesStatisticsType:
    completed = CourseSessionAttendanceCourse.objects.filter(
        course_session_id__in=course_session_selection_ids,
        due_date__end__lt=timezone.now(),
    ).order_by("-due_date__end")

    records = []

    for attendance_day in completed:
        circle = attendance_day.learning_content.get_parent_circle()

        course_session = attendance_day.course_session

        url = f"/course/{course_session.course.slug}/cockpit/attendance?id={attendance_day.learning_content.id}&courseSessionId={course_session.id}"

        participant_user_ids = [
            str(user_id)
            for user_id in CourseSessionUser.objects.filter(
                course_session=course_session, role=CourseSessionUser.Role.MEMBER
            ).values_list("user_id", flat=True)
        ]
        participants_total = len(participant_user_ids)
        participants_present = len(
            [
                participant
                for participant in attendance_day.attendance_user_list
                if participant["status"] == AttendanceUserStatus.PRESENT.value
                # in the `attendance_user_list` are users present, which are not
                # (anymore) in the course session -> so we need to filter them out
                and participant["user_id"] in participant_user_ids
            ]
        )

        records.append(
            PresenceRecordStatisticsType(
                _id=f"attendance_day:{attendance_day.id}",  # noqa
                course_session_id=course_session.id,  # noqa
                generation=course_session.generation,  # noqa
                circle_id=circle.id,  # noqa
                due_date=attendance_day.due_date.end,  # noqa
                participants_present=participants_present,  # noqa
                participants_total=participants_total,  # noqa
                details_url=url,  # noqa
            )
        )

    summary = AttendanceSummaryStatisticsType(
        _id=course_id,  # noqa
        days_completed=completed.count(),  # noqa
        participants_present=calculate_avg_participation(records),  # noqa
    )

    return AttendanceDayPresencesStatisticsType(
        summary=summary, records=records, _id=course_id  # noqa
    )


def calculate_avg_participation(records: List[PresenceRecordStatisticsType]) -> float:
    if not records:
        return 0.0

    total_ratio = 0.0
    for record in records:
        if record.participants_total == 0:
            continue
        total_ratio += float(record.participants_present) / float(
            record.participants_total
        )

    return math.ceil(total_ratio / len(records) * 100)
