from django.contrib.staticfiles.testing import StaticLiveServerTestCase
import random
from recuperateur_reseau_viaire.models import Graph_viaire_ville_information, Ville, Rassemblement_ville
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.webdriver import WebDriver
from unittest.mock import patch, ANY
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from django.core.exceptions import ObjectDoesNotExist
import re

class update_gvvi_formulaireTests(StaticLiveServerTestCase):

    selenium = None

    @classmethod
    def setUpClass(cls):
        """
        Le but de cette méthode est d'initialiser selenium avant le début des tests de la classe

        """
        super().setUpClass()
        cls.selenium = WebDriver()
        cls.selenium.implicitly_wait(10)

    @classmethod
    def tearDownClass(cls):
        """
        Le but de cette méthode est de fermer selenium à la fin des tests de la classe
        """
        cls.selenium.quit()
        super().tearDownClass()

    def setUp(self) -> None:
        """
        Le but de cette méthode est que selenium soit dans l'index du site à chaque test
        """
        self.nom_ville_test_a = "commune"
        self.nom_ville_test_b = "ville"
        self.nom_ville_test_c = "sous ville 2"
        def ajoute_ville_et_gvvi(nom_ville):
            """
            Le but de cette méthode est de créer une instance ville de nom nom ville et une instance
            Graph_viaire_ville_information ayant pour ville la ville créé.
            :param nom_ville: nom de la ville à créer
            """
            Ville.objects.create(ville_nom=nom_ville)
            valeur = random.randint(-5, -1)
            Graph_viaire_ville_information.objects.create(gvvi_cout=valeur, gvvi_robustesse=valeur,
                                                          gvvi_d2_correlation_dimension=valeur,
                                                          gvvi_d1_information_dimension=valeur,
                                                          gvvi_d0_fractal_capacite_dimension=valeur,
                                                          gvvi_longueur_moyenne_rues=valeur,
                                                          gvvi_tree_vs_complete_graph=valeur,
                                                          gvvi_org_plan_vs_organized=valeur, gvvi_connectivity=valeur,
                                                          gvvi_degre_moyen=valeur, gvvi_diametre=valeur,
                                                          gvvi_nb_intersection=valeur,
                                                          ville=Ville.objects.get(ville_nom=nom_ville))

        # création d'une instance de Ville
        ajoute_ville_et_gvvi(self.nom_ville_test_a)
        ajoute_ville_et_gvvi(self.nom_ville_test_b)
        Ville.objects.create(ville_nom=self.nom_ville_test_c)
        Rassemblement_ville.objects.create(ville_englobante=Ville.objects.get(ville_nom=self.nom_ville_test_a),
                                           ville=Ville.objects.get(ville_nom=self.nom_ville_test_b))
        Rassemblement_ville.objects.create(ville_englobante=Ville.objects.get(ville_nom=self.nom_ville_test_a),
                                           ville=Ville.objects.get(ville_nom=self.nom_ville_test_c))
        self.selenium.get('%s%s' % (self.live_server_url, '/recuperateur_reseau_viaire/'))

    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.lire_graphe')
    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.renommer_fichier_graphml')
    def test_creation_une_instance_plusieurs_communes(self, mock_renommer_fichier_graphml, mock_lire_graphe):
        """
        Cette méthode test la modification d'une ville englobante
        """
        with self.subTest("vérification bien sur la bonne page"):
            # aller sur le formulaire de modification d'une ligne
            self.selenium.find_element(By.LINK_TEXT, "Update").click()
            regexp = re.compile(r'/recuperateur_reseau_viaire/graph_id_update/\d+')
            self.assertTrue(regexp.search(self.selenium.current_url) is not None)

        # nouveau nom ville
        nouveau_nom_ville = "super commune"
        # remplir l'entrée texte avec un nom de ville
        entree_nouveau_nom_ville = self.selenium.find_element(By.CSS_SELECTOR, "#id_gvvi_nom")
        entree_nouveau_nom_ville.clear()
        entree_nouveau_nom_ville.send_keys(nouveau_nom_ville)

        # récupérer toutes les lignes du tableau
        ligne_table_gvvi_liste = self.selenium.find_element(By.CSS_SELECTOR, "#Graph_viaire_ville_information").\
            find_elements(By.CSS_SELECTOR, "tr")

        # une chance sur deux, soit on appuie sur la case à cocher, soit on remplit manuellement avec un 5
        # une liste pour retenir les entrées textes modifiées manuellement et les cases cochées
        liste_id_coche = []
        for id_ligne_table in range(1, len(ligne_table_gvvi_liste), 2):
            if random.randint(0, 1) == 0:
                liste_id_coche.append(0)
                remplissage = ligne_table_gvvi_liste[id_ligne_table].find_element(By.CSS_SELECTOR, "input")
                remplissage.clear()
                remplissage.send_keys(5)
            else:
                liste_id_coche.append(1)
                ligne_table_gvvi_liste[id_ligne_table + 1].find_element(By.CSS_SELECTOR, "input").click()

        # on coche les cases des villes de la table Rassemblement_ville une fois sur deux
        liste_nom_villes_englobees_supprime = []
        liste_nom_villes_englobees = []
        ligne_table_RV_liste = self.selenium.find_element(By.CSS_SELECTOR, "#Rassemblement_ville"). \
            find_elements(By.CSS_SELECTOR, "tr")
        for id_ligne_table in range(1, len(ligne_table_RV_liste)):
            liste_nom_villes_englobees.append(
                ligne_table_RV_liste[id_ligne_table].find_element(By.CSS_SELECTOR, "th").text)
            if id_ligne_table % 2 == 1:
                liste_nom_villes_englobees_supprime.append(ligne_table_RV_liste[id_ligne_table].find_element(By.CSS_SELECTOR, "th").text)
                ligne_table_RV_liste[id_ligne_table].find_element(By.CSS_SELECTOR, "input").click()
                liste_id_coche.append(1)
            else:
                liste_id_coche.append(0)
        # mock lire Graph pour que le graph soit None
        mock_lire_graphe.return_value = None
        # valider le formulaire
        WebDriverWait(self.selenium, 10).until(
            EC.element_to_be_clickable((By.XPATH, "//button[@class='btn btn-success']")))


        self.selenium.find_element(By.XPATH, "//button[@class='btn btn-success']").submit()

        # attendre la fin du calcul OSMnx_recuperation_graph_information en attendant l'affichage du titre
        WebDriverWait(self.selenium, 10).until(
            EC.text_to_be_present_in_element((By.CSS_SELECTOR, "h1"), "Votre action a bien été enregistré"))

        with self.subTest("formulaire valide"):
            # vérification bon titre
            self.assertEqual(self.selenium.find_element(By.CSS_SELECTOR, "h1").text, "Votre action a bien été enregistré")
            # appuyer sur le bouton de validation
            self.selenium.find_element(By.XPATH, "//a[@class='btn btn-success']").click()


        with self.subTest("retourné sur la page principale"):
            # vérifier que l'on est retournée sur la page principale
            self.assertTrue(self.selenium.current_url.endswith("/recuperateur_reseau_viaire/"))

        with self.subTest("Les instances de villes ont été modifiés"):
            # vérifier que les villes ont été modifiés
            try:
                ville = Ville.objects.get(ville_nom=nouveau_nom_ville)
            except ObjectDoesNotExist:
                self.fail("la ville " + nouveau_nom_ville + " n'a pas été modifiée")

        with self.subTest("Le fichier graphml a été modifié"):
            mock_renommer_fichier_graphml.assert_called_with(ANY, nouveau_nom_ville)

        with self.subTest("Les instances de Rassemblement_ville ont été supprimmées si nécessaire"):
            #vérifier que les Rassemblements de villes ont été supprimés ou sont encore là
            ville_englobees_instances_liste = Ville.objects.filter(ville_nom__in=liste_nom_villes_englobees)
            for nom_ville_englobee in liste_nom_villes_englobees:
                if nom_ville_englobee in liste_nom_villes_englobees_supprime:
                    self.assertRaises(ObjectDoesNotExist, Rassemblement_ville.objects.get, ville=ville_englobees_instances_liste.get(ville_nom=nom_ville_englobee))
                else :
                    try:
                        Rassemblement_ville.objects.get(ville=ville_englobees_instances_liste.get(ville_nom=nom_ville_englobee))
                    except ObjectDoesNotExist:
                        self.fail("la ville englobée " + nom_ville_englobee + " n'existe plus")

        with self.subTest("la ligne de la table Graph_viaire_ville_information à été modifié en conséquence"):
            # vérifier que la ligne dans la table Graph_viaire_ville_information a été modifié dans la base de donnée
            try:
                gvvi_instance = Graph_viaire_ville_information.objects.get(ville=ville)
            except ObjectDoesNotExist:
                self.fail("la ligne dans la table Graph_viaire_ville_information n'a pas été créé")
            for gvvi_var in dir(gvvi_instance):
                if gvvi_var.startswith("gvvi_"):
                    self.assertTrue(getattr(gvvi_instance, gvvi_var) >= 0)
                elif gvvi_var == "ville":
                    self.assertEqual(getattr(gvvi_instance, gvvi_var).ville_nom, nouveau_nom_ville)

