from unittest.mock import patch

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

from vbv_lernwelt.course.creators.test_utils import (
    add_course_session_user,
    create_course,
    create_course_session,
    create_user,
)
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
from vbv_lernwelt.notify.email.email_services import EmailTemplate


class LearningMentorInvitationTest(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.participant = create_user("participant")

    def test_create_invitation_not_member(self) -> None:
        # GIVEN
        self.client.force_login(self.participant)
        invite_url = reverse(
            "create_invitation", kwargs={"course_session_id": self.course_session.id}
        )

        # WHEN
        response = self.client.post(invite_url)

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

    @patch("vbv_lernwelt.learning_mentor.views.send_email")
    def test_create_invitation(self, mock_send_mail) -> None:
        # GIVEN
        self.client.force_login(self.participant)
        add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )
        invite_url = reverse(
            "create_invitation", kwargs={"course_session_id": self.course_session.id}
        )
        email = "test@example.com"

        # WHEN
        response = self.client.post(invite_url, data={"email": email})

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

        self.assertTrue(
            MentorInvitation.objects.filter(
                id=invitation_id,
            ).exists()
        )

        mock_send_mail.assert_called_once_with(
            recipient_email=email,
            template=EmailTemplate.LEARNING_MENTOR_INVITATION,
            template_data={
                "inviter_name": f"{self.participant.first_name} {self.participant.last_name}",
                "inviter_email": self.participant.email,
                "target_url": f"https://my.vbv-afa.ch/lernbegleitung/{self.course_session.id}/invitation/{invitation_id}",
            },
            template_language=self.participant.language,
            fail_silently=True,
        )

    def test_list_invitations(self) -> None:
        # GIVEN
        self.client.force_login(self.participant)
        participant_cs_user = add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )
        email = "test@example.com"

        MentorInvitation.objects.create(participant=participant_cs_user, email=email)

        list_url = reverse(
            "list_invitations", 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)
        self.assertEqual(
            response.data,
            [{"id": str(MentorInvitation.objects.get(email=email).id), "email": email}],
        )

    def test_delete_invitation(self) -> None:
        # GIVEN
        self.client.force_login(self.participant)
        participant_cs_user = add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )
        email = "test@example.com"

        invitation = MentorInvitation.objects.create(
            participant=participant_cs_user, email=email
        )

        delete_url = reverse(
            "delete_invitation",
            kwargs={
                "course_session_id": self.course_session.id,
                "invitation_id": invitation.id,
            },
        )

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

        # THEN
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(MentorInvitation.objects.filter(id=invitation.id).exists())

    def test_accept_invitation_invalid_course(self) -> None:
        # GIVEN
        other_course, _ = create_course("Other Test Course")
        other_course_session = create_course_session(
            course=other_course, title="Other Test Session"
        )
        participant_cs_user = add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )

        invitee = create_user("invitee")
        invitation = MentorInvitation.objects.create(
            participant=participant_cs_user, email=invitee.email
        )
        self.client.force_login(invitee)

        accept_url = reverse(
            "accept_invitation", kwargs={"course_session_id": other_course_session.id}
        )

        # WHEN
        response = self.client.post(accept_url, data={"invitation_id": invitation.id})

        # THEN
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {
                "message": "Invalid invitation",
                "code": "invalidInvitation",
            },
        )

    def test_accept_invitation_role_collision(self) -> None:
        # GIVEN
        participant_cs_user = add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )

        invitee = create_user("invitee")
        invitation = MentorInvitation.objects.create(
            participant=participant_cs_user, email=invitee.email
        )
        # Make invitee a trainer
        add_course_session_user(
            self.course_session,
            invitee,
            role=CourseSessionUser.Role.EXPERT,
        )

        self.client.force_login(invitee)

        accept_url = reverse(
            "accept_invitation", kwargs={"course_session_id": self.course_session.id}
        )

        # WHEN
        response = self.client.post(accept_url, data={"invitation_id": invitation.id})

        # THEN
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.data,
            {
                "message": "User already has a role in this course",
                "code": "existingRole",
            },
        )

    def test_accept_invitation(self) -> None:
        # GIVEN
        participant_cs_user = add_course_session_user(
            self.course_session,
            self.participant,
            role=CourseSessionUser.Role.MEMBER,
        )

        invitee = create_user("invitee")
        invitation = MentorInvitation.objects.create(
            participant=participant_cs_user, email=invitee.email
        )

        self.client.force_login(invitee)

        accept_url = reverse(
            "accept_invitation", kwargs={"course_session_id": self.course_session.id}
        )

        # WHEN
        response = self.client.post(accept_url, data={"invitation_id": invitation.id})

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(MentorInvitation.objects.filter(id=invitation.id).exists())
        self.assertTrue(
            LearningMentor.objects.filter(
                mentor=invitee, course=self.course, participants=participant_cs_user
            ).exists()
        )

        user = response.data["user"]
        self.assertEqual(user["id"], str(self.participant.id))

        self.assertEqual(response.data["course_slug"], str(self.course.slug))
