from typing import Dict, List, Optional

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

from vbv_lernwelt.assignment.models import (
    AssignmentCompletion,
    AssignmentCompletionStatus,
    AssignmentType,
)
from vbv_lernwelt.core.admin import User
from vbv_lernwelt.course.creators.test_utils import (
    add_course_session_user,
    create_assignment,
    create_assignment_learning_content,
    create_circle,
    create_course,
    create_course_session,
    create_course_session_assignment,
    create_learning_unit,
    create_user,
)
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learnpath.models import LearningUnitPerformanceFeedbackType
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback


def get_completion_for_user(
    completions: List[Dict[str, str]], user: User
) -> Optional[Dict[str, str]]:
    for completion in completions:
        if completion["user_id"] == str(user.id):
            return completion
    return None


class LearningMentorAPITest(APITestCase):
    def setUp(self) -> None:
        self.course, self.course_page = create_course("Test Course")
        self.course_session = create_course_session(course=self.course, title="Test VV")

        self.circle, _ = create_circle(title="Circle", course_page=self.course_page)

        self.mentor = create_user("mentor")
        self.participant_1 = add_course_session_user(
            self.course_session,
            create_user("participant_1"),
            role=CourseSessionUser.Role.MEMBER,
        )
        self.participant_2 = add_course_session_user(
            self.course_session,
            create_user("participant_2"),
            role=CourseSessionUser.Role.MEMBER,
        )
        self.participant_3 = add_course_session_user(
            self.course_session,
            create_user("participant_3"),
            role=CourseSessionUser.Role.MEMBER,
        )

        self.url = reverse(
            "mentor_summary", kwargs={"course_session_id": self.course_session.id}
        )

    def test_api_no_mentor(self) -> None:
        # GIVEN
        self.client.force_login(self.mentor)

        # WHEN
        response = self.client.get(self.url)

        # THEN
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_api_no_participants(self) -> None:
        # GIVEN
        self.client.force_login(self.mentor)
        LearningMentor.objects.create(
            mentor=self.mentor, course=self.course_session.course
        )

        # WHEN
        response = self.client.get(self.url)

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["participants"], [])
        self.assertEqual(response.data["assignments"], [])

    def test_api_participants(self) -> None:
        # GIVEN
        participants = [self.participant_1, self.participant_2, self.participant_3]
        self.client.force_login(self.mentor)
        mentor = LearningMentor.objects.create(
            mentor=self.mentor,
            course=self.course_session.course,
        )
        mentor.participants.set(participants)

        # WHEN
        response = self.client.get(self.url)

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data["participants"]), len(participants))

        participant_1 = [
            p
            for p in response.data["participants"]
            if p["id"] == str(self.participant_1.user.id)
        ][0]
        self.assertEqual(participant_1["email"], "participant_1@example.com")
        self.assertEqual(participant_1["first_name"], "Test")
        self.assertEqual(participant_1["last_name"], "Participant_1")

    def test_api_self_evaluation_feedback(self) -> None:
        # GIVEN
        participants = [self.participant_1, self.participant_2, self.participant_3]
        self.client.force_login(self.mentor)

        mentor = LearningMentor.objects.create(
            mentor=self.mentor,
            course=self.course_session.course,
        )

        mentor.participants.set(participants)

        learning_unit = create_learning_unit(
            circle=self.circle,
            course=self.course,
        )

        # performance criteria under this learning unit shall be evaluated by the mentor
        learning_unit.feedback_user = (
            LearningUnitPerformanceFeedbackType.MENTOR_FEEDBACK.name
        )

        learning_unit.save()

        # 1: we already evaluated
        SelfEvaluationFeedback.objects.create(
            feedback_requester_user=self.participant_1.user,
            feedback_provider_user=self.mentor,
            learning_unit=learning_unit,
            feedback_submitted=True,
        )

        # 2: we have not evaluated yet
        SelfEvaluationFeedback.objects.create(
            feedback_requester_user=self.participant_2.user,
            feedback_provider_user=self.mentor,
            learning_unit=learning_unit,
            feedback_submitted=False,
        )

        # 3: did not request feedback
        # ...

        # WHEN
        response = self.client.get(self.url)

        # THEN
        assignments = response.data["assignments"]
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertEqual(
            response.data["circles"],
            [{"id": self.circle.id, "title": self.circle.title}],
        )

        self.assertEqual(len(assignments), 1)
        assignment = assignments[0]

        self.assertEqual(assignment["type"], "self_evaluation_feedback")
        self.assertEqual(assignment["pending_evaluations"], 1)

        completions = assignment["completions"]
        self.assertEqual(
            len(completions),
            3,
        )

        completion_1 = get_completion_for_user(completions, self.participant_1.user)
        self.assertEqual(completion_1["status"], "EVALUATED")
        self.assertEqual(completion_1["last_name"], "Participant_1")
        self.assertEqual(completion_1["user_id"], str(self.participant_1.user.id))

        completion_2 = get_completion_for_user(completions, self.participant_2.user)
        self.assertEqual(completion_2["status"], "SUBMITTED")
        self.assertEqual(completion_2["last_name"], "Participant_2")
        self.assertEqual(completion_2["user_id"], str(self.participant_2.user.id))

        completion_3 = get_completion_for_user(completions, self.participant_3.user)
        self.assertEqual(completion_3["status"], "UNKNOWN")
        self.assertEqual(completion_3["last_name"], "Participant_3")
        self.assertEqual(completion_3["user_id"], str(self.participant_3.user.id))

    def test_api_praxis_assignments(self) -> None:
        # GIVEN
        self.client.force_login(self.mentor)

        assignment = create_assignment(
            course=self.course, assignment_type=AssignmentType.PRAXIS_ASSIGNMENT
        )

        lca = create_assignment_learning_content(self.circle, assignment)
        create_course_session_assignment(
            course_session=self.course_session, learning_content_assignment=lca
        )

        mentor = LearningMentor.objects.create(
            mentor=self.mentor,
            course=self.course_session.course,
        )

        participants = [self.participant_1, self.participant_2, self.participant_3]
        mentor.participants.set(participants)

        AssignmentCompletion.objects.create(
            assignment_user=self.participant_1.user,
            course_session=self.course_session,
            assignment=assignment,
            completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
            evaluation_user=self.mentor,
        )

        AssignmentCompletion.objects.create(
            assignment_user=self.participant_3.user,
            course_session=self.course_session,
            assignment=assignment,
            completion_status=AssignmentCompletionStatus.SUBMITTED.value,
            evaluation_user=self.mentor,
        )

        # WHEN
        response = self.client.get(self.url)

        # THEN
        assignments = response.data["assignments"]
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(assignments), 1)

        self.assertEqual(
            response.data["circles"],
            [{"id": self.circle.id, "title": self.circle.title}],
        )

        assignment = assignments[0]
        self.assertEqual(assignment["type"], "praxis_assignment")
        self.assertEqual(assignment["pending_evaluations"], 1)

        self.assertEqual(
            assignment["completions"][0]["last_name"], self.participant_3.user.last_name
        )
        self.assertEqual(
            assignment["completions"][1]["last_name"], self.participant_1.user.last_name
        )
        self.assertEqual(
            assignment["completions"][2]["last_name"], self.participant_2.user.last_name
        )

    def test_list_user_mentors(self) -> None:
        # GIVEN
        participant = create_user("participant")
        self.client.force_login(participant)

        participant_cs_user = add_course_session_user(
            self.course_session,
            participant,
            role=CourseSessionUser.Role.MEMBER,
        )

        learning_mentor = LearningMentor.objects.create(
            mentor=self.mentor,
            course=self.course_session.course,
        )

        learning_mentor.participants.add(participant_cs_user)

        list_url = reverse(
            "list_user_mentors", kwargs={"course_session_id": self.course_session.id}
        )

        # WHEN
        response = self.client.get(list_url)

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        mentor = response.data[0]
        self.assertEqual(mentor["id"], learning_mentor.id)

        mentor_user = mentor["mentor"]
        self.assertEqual(mentor_user["email"], self.mentor.email)
        self.assertEqual(mentor_user["id"], str(self.mentor.id))

    def test_remove_user_mentor(self) -> None:
        # GIVEN
        participant = create_user("participant")
        self.client.force_login(participant)

        participant_cs_user = add_course_session_user(
            self.course_session,
            participant,
            role=CourseSessionUser.Role.MEMBER,
        )

        learning_mentor = LearningMentor.objects.create(
            mentor=self.mentor,
            course=self.course_session.course,
        )

        learning_mentor.participants.add(participant_cs_user)

        remove_self_url = reverse(
            "remove_self_from_mentor",
            kwargs={
                "course_session_id": self.course_session.id,
                "mentor_id": learning_mentor.id,
            },
        )

        # WHEN
        response = self.client.delete(remove_self_url)

        # THEN
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(
            LearningMentor.objects.filter(participants=participant_cs_user).exists()
        )

    def test_mentor_multiple_courses(self) -> None:
        # GIVEN
        course_a, _ = create_course("Course A")
        course_b, _ = create_course("Course B")

        # WHEN
        LearningMentor.objects.create(mentor=self.mentor, course=course_a)
        LearningMentor.objects.create(mentor=self.mentor, course=course_b)

        # THEN
        self.assertEqual(LearningMentor.objects.count(), 2)
