# Fichier de configuration du plugin

from qgis.PyQt.QtWidgets import (
    QDialog,
    QMessageBox,
    QProgressDialog,
    QApplication,
    QInputDialog,
)
from .ui_config_dialog import Ui_ConfigDialog

from qgis.core import (
    QgsProject,
    QgsVectorLayer,
    QgsField,
    QgsVectorFileWriter,
    QgsCoordinateTransformContext,
)

from qgis.PyQt.QtCore import QCoreApplication, QSettings

from qgis.PyQt import QtCore, QtGui, QtWidgets

from .utils import tr, CADASTRE_FORMATS

from .cadastre_manager import (
    creer_champs_france,
    creer_champs_luxembourg,
    creer_champs_belgique,
    creer_champs_france_like,
    creer_champs_suisse
)

from .create_fields_dialog import CreateFieldsDialog

from compat_qt import (
    WindowModal,
    TYPE_MAPPING,
    exec_dialog,
    MB_Yes,
    MB_No,
    MB_Ok,
    QVariant
)
import os
import re

CHAMPS_A_CREER = [
            ("section", "String"),
            ("numero", "String"),
            ("indice_parc", "String"),
            ("contenance", "Integer64"),
            ("arpente", "Integer"),
            ("created", "Date"),
            ("updated", "Date"),
            ("SURFACE", "Real"),
            ("Possession", "Boolean"),
            ("prixAct", "Integer"),
            ("prix", "Integer"),
            ("dateAchat","Date"),
            ("Terrain", "String"),
            ("Acces","String"),
            ("RemTerrain","String"),
            ("nom_Voisin", "String"),
            ("adresse_Voisin", "String"),
            ("tel_Voisin", "String"),
            ("mail_Voisin", "String"),
            ("plant1", "String"),
            ("plant2", "String"),
            ("plant3", "String"),
            ("plant4", "String"),
            ("annee", "Integer"),
            ("totalplants", "Integer"),
            ("Tx1", "Integer"),
            ("Tx2", "Integer"),
            ("Tx3", "Integer"),
            ("Tx4", "Integer"),
            ("Tvx1", "String"),
            ("Tvx2", "String"),
            ("Tvx3", "String"),
            ("Tvx4", "String"),
            ("Tvx5", "String"),
            ("Tvx6", "String"),
            ("Trait1", "String"),
            ("Trait2", "String"),
            ("Trait3", "String"),
            ("Trait4", "String"),
            ("Trait5", "String"),
            ("Trait6", "String"),
            ("dateTvx1", "Date"),
            ("dateTvx2", "Date"),
            ("dateTvx3", "Date"),
            ("dateTvx4", "Date"),
            ("dateTvx5", "Date"),
            ("dateTvx6", "Date"),
            ("remTvx1", "String"),
            ("remTvx2", "String"),
            ("remTvx3", "String"),
            ("remTvx4", "String"),
            ("remTvx5", "String"),
            ("remTvx6", "String"),
            ("dateTrait1", "Date"),
            ("dateTrait2", "Date"),
            ("dateTrait3", "Date"),
            ("dateTrait4", "Date"),
            ("dateTrait5", "Date"),
            ("dateTrait6", "Date"),
            ("remTrait1", "String"),
            ("remTrait2", "String"),
            ("remTrait3", "String"),
            ("remTrait4", "String"),
            ("remTrait5", "String"),
            ("remTrait6", "String"),
            ("Prev1", "String"),
            ("Prev2", "String"),
            ("Prev3", "String"),
            ("Prev4", "String"),
            ("datePrev1", "String"),
            ("datePrev2", "String"),
            ("datePrev3", "String"),
            ("datePrev4", "String"),
            ("remPrev1", "String"),
            ("remPrev2", "String"),
            ("remPrev3", "String"),
            ("remPrev4", "String"),
            ("typeParc", "Integer")
        ]


class ConfigDialog(QDialog):

    def __init__(self, parent=None, iface=None):
        # Assurez-vous que le parent est un QWidget (iface.mainWindow() est un QWidget)
        super().__init__(parent)  # parent est maintenant un QWidget
        self.ui = Ui_ConfigDialog()
        self.ui.setupUi(self)

        # Sauvegarder l'instance de iface
        self.iface = iface  # On assigne iface à l'attribut self.iface

        # Connecter les événements des cases à cocher
        self.ui.checkBoxChamps.stateChanged.connect(self.update_confirm_button_state)
        self.ui.checkBoxSymbio.stateChanged.connect(self.update_confirm_button_state)
        self.ui.btnConfirm.clicked.connect(self.confirm_actions)
        self.ui.checkBoxCadastreFrench.stateChanged.connect(self.toggle_country_combo_visibility)

        # Initialiser layer_signal_connected à False avant toute utilisation
        self.layer_signal_connected = False

        # Mettre à jour immédiatement la ligne de couche
        self.update_line_layer()

        # Connexion de l'événement au changement de couche
        self.iface.currentLayerChanged.connect(self.update_line_layer)

        # Initialiser l'état du bouton à l'ouverture du dialog
        self.update_confirm_button_state()

        # Initialiser le masquage du combo des pays
        self.toggle_country_combo_visibility()

        # Afficher les champs à créer dans le TextEdit
        self.afficher_champs()

        # Initialisation du combo, exclure "FR"
        self.ui.comboCountryCadastre.clear()
        self.ui.comboCountryCadastre.addItems(CADASTRE_FORMATS)


    #    print("Widgets disponibles :", dir(self.ui))


    # Masquage combo si cadastre Français choisi
    def toggle_country_combo_visibility(self):
        is_french = self.ui.checkBoxCadastreFrench.isChecked()
        self.ui.comboCountryCadastre.setVisible(not is_french)

    def get_pays_selectionne(self):
        return "France" if self.ui.checkBoxCadastreFrench.isChecked() else self.ui.comboCountryCadastre.currentText()

    def closeEvent(self, event):
        # Déconnecter proprement le signal quand la fenêtre se ferme
        if self.iface:
            try:
                self.iface.currentLayerChanged.disconnect(self.update_line_layer)
            except TypeError:
                pass
        super().closeEvent(event)

    def update_line_layer(self):
        """Met à jour le champ lineLayer avec le nom de la couche sélectionnée et vérifie le format"""

        # Vérifier si une couche active existe
        layer = self.iface.activeLayer()
        if layer is None or not isinstance(layer, QgsVectorLayer):
            self.ui.lineLayer.clear()
            return
        # Affiche le nom de la couche active dans le champ lineLayer
        self.ui.lineLayer.setText(layer.name())

    def update_confirm_button_state(self):
        # Active ou désactive le bouton confirmer selon l'état des checkboxes et de la couche sélectionnée
        champs_ok = self.ui.checkBoxChamps.isChecked()
        symbio_ok = self.ui.checkBoxSymbio.isChecked()
        layer_text = self.ui.lineLayer.text()

        layer = self.iface.activeLayer()


        if champs_ok and symbio_ok and layer_text: # and is_gpkg:
            self.ui.btnConfirm.setEnabled(True)
        else:
            self.ui.btnConfirm.setEnabled(False)

    def champs_requis_absents(self, layer):
        """Vérifie les champs requis et propose d'en créer certains s'ils manquent.
        Pour cadastre non Français, affiche juste un message si id/contenance manquent,
        sans bloquer la suite."""

        if not layer:
            return True

        noms_existants = {f.name().lower() for f in layer.fields()}
        champs_requis = {"id", "section", "numero", "contenance"}

        champs_non_creables = {"id", "contenance"}
        manquants_non_creables = champs_non_creables - noms_existants

        champs_creables = {"section", "numero"}
        manquants_creables = champs_creables - noms_existants

        pays = self.get_pays_selectionne()

        if pays == "France":
            # comportement actuel, strict
            if manquants_non_creables:
                return True

            if manquants_creables:
                ok = self.proposer_creation_champs(layer)
                if not ok:
                    return True

            noms_final = {f.name().lower() for f in layer.fields()}
            return not champs_requis.issubset(noms_final)

        else:
            # Pour les autres cadastres, afficher un message si id/contenance manquent
            if manquants_non_creables:
                QMessageBox.information(
                    self,
                    tr("Attention"),
                    tr("Les champs 'id' et 'contenance' sont recommandés pour un bon fonctionnement.")
                )
            # On laisse passer, on ne bloque pas la suite
            return False

    def proposer_creation_champs(self, layer):
        #from .create_fields_dialog import CreateFieldsDialog
        #import re

        id_exemple = ""
        for feat in layer.getFeatures():
            id_exemple = str(feat["id"]) if "id" in layer.fields().names() else ""
            break

        if not id_exemple:
            QMessageBox.warning(
                self,
                tr("Erreur"),
                tr("Aucun identifiant trouvé.")
            )
            return False

        match = re.match(r"^\d+([A-Za-z]{1,2})0*(\d+)$", id_exemple.strip())
        if match:
            section, numero = match.groups()
            section = section.upper()
        else:
            section, numero = "?", "?"

        dlg = CreateFieldsDialog()
        dlg.ui.lineIdFormat.setText(id_exemple)
        dlg.ui.lineSectionFormat.setText(section)
        dlg.ui.lineNumFormat.setText(numero)

        result = exec_dialog(dlg)
        if result == QDialog.Accepted:
            no_section = dlg.no_section  # récupère la valeur depuis la boîte
            if dlg.resultat == "remplir":
                creer_champs_france(self, layer, remplir=True, no_section=dlg.no_section)
            elif dlg.resultat == "vide":
                creer_champs_france(self, layer, remplir=False, no_section=dlg.no_section)

            return True
        else:
            return False


    def confirm_actions(self):
        # Vérifier si les deux cases à cocher sont activées
        if not self.ui.checkBoxChamps.isChecked() and not self.ui.checkBoxSymbio.isChecked():
            QMessageBox.warning(
                self,
                tr("Avertissement"),
                tr("Veuillez cocher les deux options.")
            )
            return

        # Vérifier si une couche est sélectionnée
        layer = self.get_selected_layer()
        if not layer:
            QMessageBox.warning(
                self,
                tr("Erreur"),
                tr("Aucune couche vectorielle sélectionnée.")
            )
            return

        # Vérifier si le format est bien gpkg, sinon proposer une conversion
        path = layer.source().split("|")[0]
        _, ext = os.path.splitext(path)
        if ext.lower() != ".gpkg":
            reponse = QMessageBox.question(
                self,
                tr("Conversion recommandée"),
                tr("⚠️ Cette couche n'est pas au format GeoPackage (.gpkg).\n"
                   "Souhaitez-vous la convertir pour assurer le bon fonctionnement du plugin ?"),
                MB_Yes | MB_No
            )
            if reponse == MB_Yes:
                new_path = self.convert_layer_to_gpkg(layer)  # ⚠️ retourne un chemin
                if new_path:
                    layer_name = os.path.splitext(os.path.basename(new_path))[0]
                    new_layer = QgsVectorLayer(new_path, layer_name, "ogr")
                    if new_layer.isValid():
                        QgsProject.instance().addMapLayer(new_layer)
                        self.iface.setActiveLayer(new_layer)
                        self.ui.lineLayer.setText(new_layer.name())
                        layer = new_layer  # On travaille avec cette nouvelle couche
                    else:
                        QMessageBox.critical(
                            self,
                            tr("Erreur"),
                            tr("La couche convertie est invalide.")
                        )
                        return
                else:
                    return  # Nom déjà pris ou conversion annulée

        # Vérifier si la couche sélectionnée est affichée dans le champ lineLayer
        if not self.ui.lineLayer.text():
            QMessageBox.warning(
                self,
                tr("Erreur"),
                tr("Veuillez sélectionner une couche et la renseigner dans le champ.")
            )
            return

        # Afficher un warning uniquement si des champs requis sont absents
        if self.ui.checkBoxChamps.isChecked() and self.champs_requis_absents(layer):
            QMessageBox.warning(
                self,
                tr("Attention"),
                tr("⚠️ Un des champs 'id', 'section', 'numero' et 'contenance' n'existe pas dans la couche."),
                MB_Ok
            )

        # Demander confirmation avant de continuer
        reply = QMessageBox.question(
            self,
            tr("Confirmation"),
            tr("Voulez-vous vraiment continuer ?"),
            MB_Yes | MB_No
        )

        if reply == MB_Yes:
            if self.ui.checkBoxChamps.isChecked():
                pays = self.get_pays_selectionne()
                if pays == "France":
                    noms_existants = {f.name().lower() for f in layer.fields()}
                    champs_creables = {"section", "numero"}
                    champs_manquants = champs_creables - noms_existants
                    if champs_manquants:
                        self.proposer_creation_champs(layer)
                    else:
                        # Les champs section et numero existent, on passe directement à la suite
                        self.create_fields_table(layer)
                elif pays == "Luxembourg":
                    creer_champs_luxembourg(layer, remplir=True, parent=self)
                elif pays == "Belgique":
                    creer_champs_belgique(layer, remplir=True, parent=self)
                elif pays in ["Sénégal", "Côte d’Ivoire", "Bénin", "Burkina Faso", "Togo", "Mali", "Niger"]:
                    creer_champs_france_like(layer, remplir=True, parent=self)
                elif pays == "Suisse":
                    creer_champs_suisse(layer, remplir=True, parent=self)
                else:
                    QMessageBox.information(
                        self,
                        tr("Info"),
                        tr("Pas de traitement spécifique défini pour le cadastre : {pays}").format(pays=pays)
                    )
            if self.ui.checkBoxSymbio.isChecked():
                self.create_style_layer(layer)
            self.accept()

    def convert_layer_to_gpkg(self, layer):
        # Chemin du projet
        project_path = QgsProject.instance().fileName()
        if not project_path:
            QMessageBox.warning(
                self,
                tr("Projet non sauvegardé"),
                tr("Veuillez d'abord sauvegarder le projet QGIS.")
            )
            return None

        base_dir = os.path.dirname(project_path)
        default_name = layer.name() + "_converti"

        # Demander un nom à l'utilisateur
        new_name, ok = QInputDialog.getText(
            self,
            tr("Nom de la couche"),
            tr("Entrez le nom de la nouvelle couche :"),
            text=default_name
        )
        if not ok or not new_name.strip():
            return None

        new_name = new_name.strip()
        new_path = os.path.join(base_dir, f"{new_name}.gpkg")

        # 🔍 Vérifier si une couche de ce nom existe déjà dans le projet
        if new_name in [lyr.name() for lyr in QgsProject.instance().mapLayers().values()]:
            QMessageBox.warning(
                self,
                tr("Nom déjà utilisé"),
                tr("Une couche nommée « {name} » existe déjà dans le projet.").format(name=new_name)
            )
            return None

        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = "GPKG"
        options.fileEncoding = "UTF-8"
        options.layerName = new_name

        context = QgsProject.instance().transformContext()
        error = QgsVectorFileWriter.writeAsVectorFormatV2(layer, new_path, context, options)

        print(">>> Code retour de conversion :", error[0], error[1])
        if error[0] != QgsVectorFileWriter.NoError:
            QMessageBox.critical(
                self,
                tr("Erreur"),
                tr("La conversion a échoué.")
            )
            return None

        return new_path

    def get_selected_layer(self):
        # Renvoie la couche vectorielle active, ou None sinon
        layer = self.iface.activeLayer()
        if isinstance(layer, QgsVectorLayer):
            return layer
        return None

    def create_fields_table(self, layer):
        if not layer:
            QMessageBox.warning(self, tr("Avertissement"), tr("Aucune couche sélectionnée."))
            return

        self.ui.lineLayer.setText(layer.name())
        noms_existants = {f.name() for f in layer.fields()}

        try:
            champs_manquants = []
            for nom, typ in CHAMPS_A_CREER:
                if nom in noms_existants:
                    continue

                if nom == "prix":
                    # Réglage spécial pour avoir un champ flottant avec 2 décimales
                    champ = QgsField(nom, QVariant.Int, "int", len=6)
                else:
                    champ = QgsField(nom, TYPE_MAPPING[typ])

                champs_manquants.append(champ)

        except KeyError as e:
            message = tr("Type de champ non reconnu : %1").replace("%1", str(e))
            QMessageBox.critical(self, tr("Erreur"), message)
            return

        if not champs_manquants:
            QMessageBox.information(self, tr("Information"), tr("Tous les champs sont déjà présents."))
            return

        progress = QProgressDialog(tr("Ajout des champs..."), tr("Annuler"), 0, len(champs_manquants), self)
        progress.setWindowTitle(tr("Traitement en cours"))
        progress.setWindowModality(WindowModal)
        progress.setMinimumDuration(0)

        layer.startEditing()
        success = True

        # Ajout des champs un par un avec progression
        for i, field in enumerate(champs_manquants):
            if not layer.dataProvider().addAttributes([field]):
                success = False
                break
            progress.setValue(i + 1)
            QApplication.processEvents()

        if not success:
            layer.rollBack()
            progress.close()
            QMessageBox.warning(self, tr("Erreur"), tr("Échec lors de la création des champs."))
            return

        layer.updateFields()

        # Mise à jour du champ SURFACE si nécessaire
        if "contenance" in noms_existants and "SURFACE" in {f.name() for f in layer.fields()}:
            progress.setLabelText(tr("Mise à jour du champ SURFACE..."))
            feats = list(layer.getFeatures())
            total = len(feats)
            idx_contenance = layer.fields().indexFromName("contenance")
            idx_surface = layer.fields().indexFromName("SURFACE")

            for i, feat in enumerate(feats):
                val_contenance = feat[idx_contenance]
                if val_contenance not in (None, ''):
                    try:
                        val_surface = float(val_contenance) / 100.0
                        layer.changeAttributeValue(feat.id(), idx_surface, val_surface)
                    except Exception as e:
                        print(f"Erreur conversion contenance: {e}")
                if i % 20 == 0:  # Rafraîchir la progression toutes les 20 entités
                    progress.setMaximum(total)
                    progress.setValue(i)
                    QApplication.processEvents()

        layer.commitChanges()
        progress.close()
        QMessageBox.information(self, tr("Succès"), tr("Champs créés avec succès."))

    def create_style_layer(self, layer):

        # Langue de l'utilisateur (ex : 'fr', 'en')
        langue = QSettings().value("locale/userLocale", "fr")[0:2]

        # Construction du chemin vers le fichier .qml correspondant
        qml_filename = f"styleplugin_{langue}.qml"
        qml_path = os.path.join(os.path.dirname(__file__), "ressources", qml_filename)

        if os.path.exists(qml_path):
            layer.loadNamedStyle(qml_path)
            layer.triggerRepaint()
            QMessageBox.information(
                self,
                tr("Style appliqué"),
                tr("Le style '{nom_fichier}' a été appliqué.").format(nom_fichier=qml_filename)
            )
        else:
            QMessageBox.warning(
                self,
                tr("Erreur"),
                tr("Le fichier de style '{nom_fichier}' n'a pas été trouvé.").format(nom_fichier=qml_filename)
            )



    def afficher_champs(self):
        # Affiche la liste des champs dans le QTextEdit avec formatage
        texte = ""
        for nom, typ in CHAMPS_A_CREER:
            texte += f"- {nom} : {typ}\n"
        self.ui.textEditChamps.setPlainText(texte)
