from unittest import mock

from backend.models import Administre, Decision, DecisionChoices, Poste
from backend.models import StatutPamChoices as StatutPam
from backend.utils.decisions import (KEY_CREATE, KEY_UPDATE,
                                     get_available_decisions)
from backend.utils.permissions import (KEY_READ, KEY_WRITE, Profiles,
                                       get_profiles_by_adm)
from backend.views import DecisionView
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase

from .constants import PASSWORD, USERNAME
from .test_utils import TestUtilsMixin, disable_gestionnaire_permission

PK_SV_1 = 'SV1'
VIEW_TYPE = DecisionView

class DecisionViewTest(APITestCase, TestUtilsMixin):

    basename = 'Decision'

    @classmethod
    def setUpTestData(cls):
        user = get_user_model().objects.create(id=1, username=USERNAME, is_superuser=True)
        user.set_password(PASSWORD)
        user.save()

    def setUp(self):
        """ vérifie qu'il n'existe aucun administré ou poste avant le test """

        self.assertEqual(Administre.objects.exists(), False, "pas d'administré avant un test")
        self.assertEqual(Poste.objects.exists(), False, "pas de poste avant un test")
        logged_in = self.client.login(username=USERNAME, password=PASSWORD)
        self.assertTrue(logged_in, "l'utilisateur devrait être connecté")

    def tearDown(self):
        """ supprime tous les administrés, les postes, les décisions en fin de test """

        self.client.logout()
        Decision.objects.all().delete()
        Poste.objects.all().delete()
        Administre.objects.all().delete()

    def test_list_anonymous(self):
        """ vérifie que l'accès anonyme est interdit """

        self.client.logout()
        url = self._api_url()

        response = self.client.get(url)

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

    @disable_gestionnaire_permission(VIEW_TYPE)
    def test_list_authenticated(self):
        """ vérifie que l'utilisateur authentifié peut récupérer des décisions """

        url = self._api_url()

        response = self.client.get(url)

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

    @disable_gestionnaire_permission(VIEW_TYPE)
    def test_crud(self):
        """ test de création, MAJ, suppression """
        try:
            statut_pam = next(x for x in StatutPam if x.dec_enabled)
            self.assertIsNotNone(statut_pam, 'il devrait exister un statut permettant les décisions')
            self.user = get_user_model().objects.first()
            self.adm = Administre.objects.create(pk=1, a_statut_pam=statut_pam)

            @mock.patch(
                f'{get_available_decisions.__module__}.{get_profiles_by_adm.__name__}',
                return_value={self.adm.pk: {KEY_READ: (), KEY_WRITE: (Profiles.FILIERE, Profiles.PCP)}}
            )
            def do_with_mock_profiles(mock):
                user = self.user
                adm = self.adm
                decisions = get_available_decisions((adm,), user=user).get(adm.pk)
                self.assertTrue(decisions.get(KEY_CREATE), 'il devrait exister des décisions possibles (création)')

                def create(poste_id, de_decision, adm_id=adm.pk, delete_former=None):
                    return self.client.post(
                        self._api_url(),
                        {'administre_id': adm_id, 'poste_id': poste_id, 'de_decision': de_decision, **({'delete_former': delete_former} if isinstance(delete_former, bool) else {})}
                    )

                postes = (Poste.objects.create(pk='11'), Poste.objects.create(pk='13'))
                poste_1 = postes[0].pk
                poste_2 = postes[1].pk
                dec_status_1 = decisions.get(KEY_CREATE)[0]

                # création initiale
                response = create(poste_1, dec_status_1)

                qs_decision1 = Decision.objects.filter(pk=adm.pk, poste_id=poste_1, de_decision=dec_status_1)
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
                self.assertTrue(qs_decision1.exists(), "la décision n'a pas les bonnes données")

                # création bis, pas possible sans forcer
                response = create(poste_2, dec_status_1)

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
                self.assertTrue(qs_decision1.exists(), "la première décision doit encore exister")

                # création bis en forçant
                notes = 'notes'
                qs_decision1.update(de_notes_gestionnaire='notes')
                self.assertEqual(notes, qs_decision1.first().de_notes_gestionnaire, "les notes doivent être sauvegardées pour le prochain test")

                response = create(poste_2, dec_status_1, delete_former=True)

                qs_decision2 = Decision.objects.filter(pk=adm.pk, poste_id=poste_2, de_decision=dec_status_1)
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
                self.assertFalse(qs_decision1.exists(), "la première décision ne doit plus exister")
                self.assertTrue(qs_decision2.exists(), "la deuxième décision n'a pas les bonnes données")
                self.assertIsNone(qs_decision2.first().de_notes_gestionnaire, "il ne doit plus exister de notes dans la deuxième décision")

                # MAJ
                adm = Administre.objects.filter(pk=adm.pk).first()
                decisions = get_available_decisions((adm,), user=user).get(adm.pk)
                self.assertTrue(decisions.get(KEY_UPDATE), 'il devrait exister des décisions possibles (MAJ) pour le prochain test')
                dec_status_2 = decisions.get(KEY_UPDATE)[0]

                response = self.client.patch(self._api_url(adm.pk), data={'de_decision': dec_status_2})

                qs_decision3 = Decision.objects.filter(pk=adm.pk, poste_id=poste_2, de_decision=dec_status_2)
                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
                self.assertTrue(qs_decision3.exists(), "la deuxième décision doit changer de statut")

                # suppression
                response = self.client.delete(self._api_url(adm.pk))

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
                self.assertEqual(0, Decision.objects.count(), "il ne doit plus exister de décision")

            do_with_mock_profiles()
        finally:
            self.user = None
            self.adm = None
