import graphene
from graphene import Enum

from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.dashboard.graphql.types.assignment import (
    assignments,
    AssignmentsStatisticsType,
)
from vbv_lernwelt.dashboard.graphql.types.attendance import (
    attendance_day_presences,
    AttendanceDayPresencesStatisticsType,
)
from vbv_lernwelt.dashboard.graphql.types.competence import (
    CompetencePerformanceStatisticsSummaryType,
    competences,
    CompetencesStatisticsType,
)
from vbv_lernwelt.dashboard.graphql.types.feedback import (
    feedback_responses,
    FeedbackStatisticsResponsesType,
)
from vbv_lernwelt.learnpath.models import Circle


class StatisticsCourseSessionDataType(graphene.ObjectType):
    id = graphene.ID(required=True)
    name = graphene.String(required=True)


class StatisticsCircleDataType(graphene.ObjectType):
    id = graphene.ID(required=True)
    name = graphene.String(required=True)


class StatisticsCourseSessionsSelectionMetricType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    session_count = graphene.Int(required=True)
    participant_count = graphene.Int(required=True)
    expert_count = graphene.Int(required=True)


class StatisticsCourseSessionPropertiesType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    sessions = graphene.List(
        graphene.NonNull(StatisticsCourseSessionDataType), required=True
    )
    generations = graphene.List(graphene.NonNull(graphene.String), required=True)
    circles = graphene.List(graphene.NonNull(StatisticsCircleDataType), required=True)


class DashboardType(Enum):
    STATISTICS_DASHBOARD = "StatisticsDashboard"
    PROGRESS_DASHBOARD = "ProgressDashboard"
    SIMPLE_DASHBOARD = "SimpleDashboard"


class DashboardConfigType(graphene.ObjectType):
    id = graphene.ID(required=True)
    name = graphene.String(required=True)
    slug = graphene.String(required=True)
    dashboard_type = graphene.Field(DashboardType, required=True)


class ProgressDashboardCompetenceType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    total_count = graphene.Int(required=True)
    success_count = graphene.Int(required=True)
    fail_count = graphene.Int(required=True)


class ProgressDashboardAssignmentType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    total_count = graphene.Int(required=True)
    points_max_count = graphene.Int(required=True)
    points_achieved_count = graphene.Int(required=True)


class CourseProgressType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    course_id = graphene.ID(required=True)
    session_to_continue_id = graphene.ID(required=False)
    competence = graphene.Field(ProgressDashboardCompetenceType, required=True)
    assignment = graphene.Field(ProgressDashboardAssignmentType, required=True)


class CourseStatisticsType(graphene.ObjectType):
    _id = graphene.ID(required=True)
    course_id = graphene.ID(required=True)
    course_title = graphene.String(required=True)
    course_slug = graphene.String(required=True)
    course_session_properties = graphene.Field(
        StatisticsCourseSessionPropertiesType, required=True
    )
    course_session_selection_ids = graphene.List(graphene.ID, required=True)
    course_session_selection_metrics = graphene.Field(
        StatisticsCourseSessionsSelectionMetricType, required=True
    )
    attendance_day_presences = graphene.Field(
        AttendanceDayPresencesStatisticsType, required=True
    )
    feedback_responses = graphene.Field(FeedbackStatisticsResponsesType, required=True)
    assignments = graphene.Field(AssignmentsStatisticsType, required=True)
    competences = graphene.Field(CompetencesStatisticsType, required=True)

    def resolve_attendance_day_presences(
        root, info
    ) -> AttendanceDayPresencesStatisticsType:
        return attendance_day_presences(
            course_id=root.course_id,
            course_session_selection_ids=root.course_session_selection_ids,
        )

    def resolve_feedback_responses(root, info) -> FeedbackStatisticsResponsesType:
        return feedback_responses(
            course_session_selection_ids=root.course_session_selection_ids,
            course_id=root.course_id,
            course_slug=root.course_slug,
        )

    def resolve_competences(root, info) -> CompetencesStatisticsType:
        records, success_total, fail_total = competences(
            course_slug=str(root.course_slug),
            course_session_selection_ids=[
                str(cs) for cs in root.course_session_selection_ids  # noqa
            ],
        )
        return CompetencesStatisticsType(
            _id=root._id,  # noqa
            records=records,  # noqa
            summary=CompetencePerformanceStatisticsSummaryType(  # noqa
                _id=root._id,  # noqa
                success_total=success_total,  # noqa
                fail_total=fail_total,  # noqa
            ),
        )

    def resolve_assignments(root, info) -> AssignmentsStatisticsType:
        return assignments(
            course_id=root.course_id,
            course_session_selection_ids=root.course_session_selection_ids,
        )

    def resolve_course_session_selection_metrics(
        root, info
    ) -> StatisticsCourseSessionsSelectionMetricType:
        course_session_count = CourseSession.objects.filter(
            id__in=root.course_session_selection_ids,
            course_id=root.course_id,
        ).count()

        expert_count = CourseSessionUser.objects.filter(
            course_session_id__in=root.course_session_selection_ids,
            role=CourseSessionUser.Role.EXPERT,
        ).count()

        participant_count = CourseSessionUser.objects.filter(
            course_session_id__in=root.course_session_selection_ids,
            role=CourseSessionUser.Role.MEMBER,
        ).count()

        return StatisticsCourseSessionsSelectionMetricType(
            _id=root._id,  # noqa
            session_count=course_session_count,  # noqa
            participant_count=participant_count,  # noqa
            expert_count=expert_count,  # noqa
        )

    def resolve_course_session_properties(root, info):
        course_session_data = []
        circle_data = []
        generations = set()

        course_sessions = CourseSession.objects.filter(
            id__in=root.course_session_selection_ids,
            course_id=root.course_id,
        )

        for course_session in course_sessions:
            course_session_data.append(
                StatisticsCourseSessionDataType(
                    id=course_session.id,  # noqa
                    name=course_session.title,  # noqa
                )
            )
            generations.add(course_session.generation)

            circles = (
                course_session.course.get_learning_path()
                .get_descendants()
                .live()
                .specific()
                .exact_type(Circle)
            )

            for circle in circles:
                if not any(c.id == circle.id for c in circle_data):
                    circle_data.append(
                        StatisticsCircleDataType(
                            id=circle.id,  # noqa
                            name=circle.title,  # noqa
                        )
                    )

        return StatisticsCourseSessionPropertiesType(
            _id=root._id,  # noqa
            sessions=course_session_data,  # noqa
            generations=list(generations),  # noqa
            circles=circle_data,  # noqa
        )
