from unittest import mock
from unittest.mock import patch, ANY

import osmnx as ox
from OSM_outils.OMSnx_enregistrement import OMSnx_enregistrement
from django.conf import settings
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core.exceptions import ObjectDoesNotExist
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 selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


class creation_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.selenium.get('%s%s' % (self.live_server_url, '/recuperateur_reseau_viaire/'))

    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.sauvegarde_graphique')
    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.chemin_vers_fichier_ville', return_value=settings.BASE_DIR.__str__() + "/recuperateur_reseau_viaire/Tests/Data/Vaudherland.graphml")
    def test_creation_une_instance_gvvi_simple(self, mock_sauvegarde_graphique, mock_chemin_vers_fichier_ville):
        """
        Cette méthode test si nous pouvons créer une nouvelle ligne Graph_viaire_ville_information depuis une base vide
        """
        nom_ville = "Vaudherland"

        with self.subTest("bouton emmènne au bon endroit"):
            # aller sur le formulaire de création de nouvelle ligne
            self.selenium.find_element(
                By.LINK_TEXT, "Ajout"
            ).click()
            self.assertTrue(self.selenium.current_url.endswith("/recuperateur_reseau_viaire/telecharge_liste_commune"))

        # remplir l'entrée texte avec un nom de ville
        entree_nom_ville = self.selenium.find_element(By.CSS_SELECTOR, "tr").find_element(By.CSS_SELECTOR, "input")
        entree_nom_ville.clear()
        entree_nom_ville.send_keys(nom_ville)

        # mock la récupération de graph pour faire croire qu'il est téléchargé depuis overpass mais est sur le pc
        # mock la sauvegarde pour ne pas sauvegarder le graph
        # mock lire_graph pour lire le fichier dans le dossier Data de Test
        #valider le formulaire
        with mock.patch.object(OMSnx_enregistrement, "lire_graphe",
                               side_effect=[FileNotFoundError, ValueError]):
            WebDriverWait(self.selenium, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[@class='btn btn-success']")))
            entree_nom_ville.submit()


        with self.subTest("vérifier que le formulaire est valide après avoir attendue que les calculs se finissent"):
            WebDriverWait(self.selenium, 10).until(
                EC.text_to_be_present_in_element((By.CSS_SELECTOR, "h1"), "Votre action a bien été enregistré"))
            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()

        #vérifier que le graph a été sauvegardé une fois
        # mock called twice
        mock_sauvegarde_graphique.assert_called_with(nom_ville)
        # 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("Ville créée dans la base d donnée"):
            #vérifier que la ville a été ajouté dans la base de donnée
            try:
                ville = Ville.objects.get(ville_nom=nom_ville)
            except ObjectDoesNotExist:
                self.fail("la ville n'a pas été créé")

        with self.subTest("Vérifier que la ligne a été ajoutée dans la table Graph_viaire_ville_information"):
            # vérifier que la ligne dans la table Graph_viaire_ville_information a été ajouté dans la base de donnée
            try:
                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éé")

        # vérifier que rien n'a été rajouter dans la table Rassemblement_ville
        self.assertRaises(ObjectDoesNotExist, Rassemblement_ville.objects.get, ville_englobante=ville)

    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.sauvegarde_graphique')
    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.lire_graphe')
    @patch('OSM_outils.OMSnx_enregistrement.OMSnx_enregistrement.recuperer_graphe_viaire_ville')
    def test_creation_une_instance_plusieurs_communes(self, mock_recuperer_graphe_viaire_ville,
                                                      mock_lire_graphe, mock_sauvegarde_graphique):
        """
        Cette méthode test si nous pouvons créer une nouvelle ligne Graph_viaire_ville_information depuis une base vide
        """

        with self.subTest("bouton emmènne au bon endroit"):
            # aller sur le formulaire de création de nouvelle ligne
            self.selenium.find_element(
                By.LINK_TEXT, "Ajout"
            ).click()
            self.assertTrue(self.selenium.current_url.endswith("/recuperateur_reseau_viaire/telecharge_liste_commune"))

        # nom de l'ensemble des communes
        nom_ville = "Mâconnais-Beaujolais Agglomération morceau"
        # remplir l'entrée texte avec un nom de ville
        entree_nom_ville = self.selenium.find_element(By.CSS_SELECTOR, "tr").find_element(By.CSS_SELECTOR, "input")
        entree_nom_ville.clear()
        entree_nom_ville.send_keys(nom_ville)

        #    #nom des communes
        liste_nom_villes_englobees = ["Mâcon", "Sancé", "Saint-Laurent-sur-Saône"]
        for _ in liste_nom_villes_englobees:
            # ajoute une entrée de ville en appuyant sur le bouton
            self.selenium.find_element(By.CLASS_NAME, "ajoute_liste_ligne").click()

        # remplissage de la zone de texte des villes
        entree_nom_ville_englobees = self.selenium.find_elements(By.XPATH, "//input[@name='gvvi_nom_ajout']")
        for id_ville_englobees in range(len(entree_nom_ville_englobees)):
            entree_nom_ville_englobees[id_ville_englobees].clear()
            entree_nom_ville_englobees[id_ville_englobees].send_keys(liste_nom_villes_englobees[id_ville_englobees])

        # mock la récupération de graph pour faire croire qu'il est téléchargé depuis overpass mais est sur le pc
        # mock la sauvegarde pour ne pas sauvegarder le graph
        # mock lire_graph pour lire le fichier dans le dossier Data de Test
        mock_lire_graphe.side_effect = FileNotFoundError()
        mock_recuperer_graphe_viaire_ville.return_value = None

        # valider le formulaire
        WebDriverWait(self.selenium, 10).until(
            EC.element_to_be_clickable((By.XPATH, "//button[@class='btn btn-success']")))
        entree_nom_ville.submit()

        with self.subTest("vérifier que le formulaire est valide après avoir attendue que les calculs se finissent"):
            WebDriverWait(self.selenium, 10).until(
                EC.text_to_be_present_in_element((By.CSS_SELECTOR, "h1"), "Votre action a bien été enregistré"))
            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("Sauvegarde du graph"):
            # vérifier que le graph a été sauvegardé une fois
            mock_sauvegarde_graphique.assert_called_once_with(ANY, nom_ville)

        with self.subTest("de retour sur la page principale"):
            self.assertTrue(self.selenium.current_url.endswith("/recuperateur_reseau_viaire/"))

        with self.subTest("Les villes ont été rajoutées dans la table Ville"):
            # vérifier que les villes ont été ajoutées dans la base de donnée
            try:
                ville = Ville.objects.get(ville_nom=nom_ville)
            except ObjectDoesNotExist:
                self.fail("la ville " + nom_ville + " n'a pas été créé")


        with self.subTest("Lignes ajoutées dans la table Rassemblement_ville"):
            ville_englobees_instances_liste = []
            for nom_ville_englobee in liste_nom_villes_englobees:
                try:
                    ville_englobees_instances_liste.append(Ville.objects.get(ville_nom=nom_ville_englobee))
                except ObjectDoesNotExist:
                    self.fail("la ville englobée " + nom_ville_englobee + " n'a pas été créé")

        with self.subTest("Ligne ajoutée dans la table Graph_viaire_ville_information"):
            # vérifier que la ligne dans la table Graph_viaire_ville_information a été ajouté dans la base de donnée
            try:
                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éé")

        # vérifier que les villes englobées ont été ajoutées dans la base de donnée
        for ville_englobee in ville_englobees_instances_liste:
            try:
                Rassemblement_ville.objects.get(ville=ville_englobee)
            except ObjectDoesNotExist:
                self.fail("la ville englobée " + ville_englobee.ville_nom + " n'a pas été créé")
        rassemblement_villes_instance_liste = Rassemblement_ville.objects.filter(ville_englobante=ville)
        self.assertTrue(rassemblement_villes_instance_liste.exists())
        self.assertEqual(len(rassemblement_villes_instance_liste), len(liste_nom_villes_englobees))
        #vérifier que les valeurs sont les bonnes
        liste_ville_nom_copy = liste_nom_villes_englobees.copy()
        for rassemblement_villes_instance in rassemblement_villes_instance_liste:
            self.assertTrue(rassemblement_villes_instance.ville.ville_nom, liste_nom_villes_englobees)
            liste_ville_nom_copy.remove(rassemblement_villes_instance.ville.ville_nom)
