from qgis.PyQt.QtWidgets import QDialog, QMessageBox


from qgis.core import (
    QgsProject,
    QgsFeatureRequest,
    QgsGeometry,
    Qgis,
    QgsVectorLayer,
    QgsMessageLog,
    QgsWkbTypes,
    QgsCoordinateReferenceSystem,
    QgsCoordinateTransform,
)
from qgis.utils import iface

from .utils import tr

from .constantes import TYPE_PARC_LIBELLES

from .compat_qt import MB_Yes, MB_No

class InfosPolygonManager:
    def __init__(self, ui, dialog):
        self.ui = ui
        self.dialog = dialog  # Ce sera maintenant un CreatePolygonDialog
        self.layer = None
        self.existing_filles = {}  # Dictionnaire des entités filles persistantes

        # Connexions pour activer/désactiver le bouton en fonction des champs obligatoires
        self.ui.lineSection.textChanged.connect(self.update_validate_button_state)
        self.ui.lineNumero.textChanged.connect(self.update_validate_button_state)
        self.ui.lineIndice.textChanged.connect(self.update_validate_button_state)

        # Connexion des boutons de Valider et Annuler
        self.ui.btnValidatePol.clicked.connect(self.save_polygon_info)
        self.ui.btnCancelPol.clicked.connect(self.cancel_polygon_creation)


        # Initialisation de l’état du bouton
        self.update_validate_button_state()

        # Initialisation des attributs liés aux champs de l'entité parente
        self.section = None
        self.numero = None
        self.commune = None
        self.prefixe = None
        self.indice_parc = ''

        self.current_feature_id = None

    def set_parent_fields(self, parent_feature):
        # Cette méthode remplit les attributs à partir de la feature parent
        self.section = parent_feature['section']
        self.numero = parent_feature['numero']
        self.commune = parent_feature['commune']
        self.prefixe = parent_feature['prefixe']
        self.indice_parc = parent_feature['indice_parc'] if 'indice_parc' in parent_feature.fields().names() and \
                                                            parent_feature['indice_parc'] else ''

    def connect_to_layer(self, layer=None):
        """Connexion au signal d’ajout de géométrie et suppression du formulaire natif."""
        QgsMessageLog.logMessage(tr("Méthode connect_to_layer appelée"), "gestion_forestiere", Qgis.Info)

        self.layer = layer or iface.activeLayer()
        if not self.layer or not isinstance(self.layer, QgsVectorLayer):
            QgsMessageLog.logMessage(tr("Aucune couche polygonale active"), "gestion_forestiere", Qgis.Warning)
            return

        # Vérifier que la géométrie est polygonale
        if self.layer.geometryType() != QgsWkbTypes.PolygonGeometry:
            QgsMessageLog.logMessage(tr("La couche active n'est pas polygonale"), "gestion_forestiere", Qgis.Warning)
            return

        # Supprimer le formulaire natif QGIS (empêcher ouverture auto)
        try:
            config = self.layer.editFormConfig()
            done = False

            # Qt6 / QGIS >= 3.37 : suppression du formulaire via flags
            if hasattr(config, "flags") and hasattr(config, "setFlags"):
                flags = config.flags()
                if hasattr(config, "FlagSuppressForm"):
                    flags |= config.FlagSuppressForm
                    config.setFlags(flags)
                    done = True

            # Qt5 / QGIS 3.22–3.36
            elif hasattr(config, "setSuppressAttributeForm"):
                config.setSuppressAttributeForm(True)
                done = True

            if not done:
                QgsMessageLog.logMessage(
                    tr("Impossible de supprimer le formulaire d'édition : aucune méthode trouvée."),
                    "gestion_forestiere", Qgis.Warning
                )

            self.layer.setEditFormConfig(config)
            QgsMessageLog.logMessage(tr("[InfosPolygonManager] Formulaire natif désactivé"),
                                     "gestion_forestiere", Qgis.Info)

        except Exception as e:
            QgsMessageLog.logMessage(
                tr("Erreur lors de la suppression du formulaire natif : {err}").format(err=e),
                "gestion_forestiere", Qgis.Warning
            )

    def disconnect_from_layer(self):
        """Déconnecte le signal pour éviter les fuites mémoire."""
        if self.layer:
            try:
                self.layer.featureAdded.disconnect(self.on_feature_added)
                QgsMessageLog.logMessage(tr("Signal featureAdded déconnecté"), "gestion_forestiere", Qgis.Info)
            except Exception:
                pass
            self.layer = None

    def on_feature_added(self, fid):
        """Handler appelé automatiquement quand un polygone est ajouté."""
        QgsMessageLog.logMessage(tr("Entité ajoutée avec l'ID {fid}").format(fid=fid),
                                 "gestion_forestiere", Qgis.Info
                                 )

        # 🔒 Sécurité : la couche n'existe plus → on sort
        if self.layer is None:
            QgsMessageLog.logMessage(
                tr("Aucune couche active dans on_feature_added — annulation."),
                "gestion_forestiere", Qgis.Warning
            )
            return

        # Récupérer la feature ajoutée
        feat = None
        for f in self.layer.getFeatures(QgsFeatureRequest(fid)):
            feat = f
            break

        if not feat:
            QgsMessageLog.logMessage(tr("Entité {fid} introuvable").format(fid=fid), "gestion_forestiere", Qgis.Warning)
            return

        # Ouvrir la fenêtre avec la géométrie de la feature ajoutée
        self.open_dialog_from_geometry(feat.geometry(), self.layer, fid)

    def open_dialog_from_geometry(self, geom, layer, fid_enfant):
        self.fid = fid_enfant
        self.layer = layer

        if not layer or not isinstance(layer, QgsVectorLayer):
            return

        if layer.geometryType() != QgsWkbTypes.PolygonGeometry:
            return

        # Trouver la géométrie contenant le nouveau polygone (i.e. la parcelle mère)
        parent_feature = None
        min_distance = float('inf')

        for f in layer.getFeatures():
            if f.id() == fid_enfant:
                continue
            if f.geometry().intersects(geom):
                dist = f.geometry().distance(geom.centroid())
                if dist < min_distance:
                    min_distance = dist
                    parent_feature = f

        if not parent_feature:
            QgsMessageLog.logMessage(tr("❌ Aucun parent trouvé pour le trou"), "gestion_forestiere", Qgis.Warning)
            return


        QgsMessageLog.logMessage(
            f"Parent trouvé: section={self.section}, numero={self.numero}, commune={self.commune}, "
            f"prefixe={self.prefixe}, indice_parc={self.indice_parc}",
            "coord_click",
            Qgis.Info
        )

        # Extraction des champs
        self.section = parent_feature['section']
        self.numero = parent_feature['numero']
        self.commune = parent_feature['commune']
        self.prefixe = parent_feature['prefixe']
        self.indice_parc = parent_feature['indice_parc'] if 'indice_parc' in parent_feature.fields().names() and \
                                                            parent_feature['indice_parc'] else ''

        # Formatage uniquement pour l’ID
        numero_id = str(self.numero).zfill(4)  # Ajoute des zéros à gauche uniquement pour l’ID

        # Construction de l’ID unique avec "0" si section a une seule lettre
        section_str = str(self.section)
        zero = "0" if len(section_str) == 1 else ""
        parc_id = f"{self.commune}{self.prefixe}{zero}{self.section}{numero_id}{self.indice_parc}"

        # Calcul de surface en ares avec reprojection vers un CRS métrique
        surface_ares = 0.0
        try:
            crs_metre = QgsCoordinateReferenceSystem("EPSG:2154")
            transformer = QgsCoordinateTransform(layer.crs(), crs_metre, QgsProject.instance())
            geom_proj = QgsGeometry(geom)
            geom_proj.transform(transformer)
            surface_m2 = geom_proj.area()
            surface_ares = round(surface_m2 / 100, 2)
            self.surface_m2 = surface_m2  # On mémorise la valeur en m² pour la réutiliser
        except Exception as e:
            QgsMessageLog.logMessage(tr("⚠️ Erreur lors du calcul de surface : {err}").format(err=e), "gestion_forestiere", Qgis.Warning)

        # --- Remplissage de la combobox combo_plantation ---
        self.ui.combo_plantation.clear()

        # TYPE_PARC_LIBELLES est un dict {numero: libelle}
        for key in sorted(TYPE_PARC_LIBELLES.keys()):
            label = TYPE_PARC_LIBELLES[key]
            self.ui.combo_plantation.addItem(label, key)  # label affiché, key stocké

        # Pré-selection du premier élément par défaut
        if self.ui.combo_plantation.count() > 0:
            self.ui.combo_plantation.setCurrentIndex(1)


        # Remplissage du formulaire
        self.ui.lineSection.setText(str(self.section))
        self.ui.lineSection.setReadOnly(True)
        self.ui.lineNumero.setText(str(self.numero))
        self.ui.lineNumero.setReadOnly(True)
        self.ui.lineId.setText(str(parc_id))
        self.ui.lineId.setReadOnly(True)
        self.ui.lineLayerName.setText(self.layer.name())
        self.ui.lineSurface.setText(f"{surface_ares:.2f} ares")
        self.ui.lineFid.setText(str(fid_enfant))
        self.ui.lineFid.setReadOnly(True)


    # Masque le bouton tant que les champs ne sont pas renseignés
    def update_validate_button_state(self):
        section = self.ui.lineSection.text().strip()
        numero = self.ui.lineNumero.text().strip()
        indice = self.ui.lineIndice.text().strip()
        enable = bool(indice)
        self.ui.btnValidatePol.setEnabled(enable)

    def save_polygon_info(self):
        section = self.ui.lineSection.text().strip()
        numero = self.ui.lineNumero.text().strip()
        indice = self.ui.lineIndice.text().strip()
        possession = self.ui.checkBoxProp.isChecked()

        # Vérification des champs obligatoires
        if not section:
            QMessageBox.warning(
                self.dialog,
                tr("Erreur"),
                tr("Le champ 'Section' est obligatoire.")
            )
            return

        if not numero:
            QMessageBox.warning(
                self.dialog,
                tr("Erreur"),
                tr("Le champ 'Numéro' est obligatoire.")
            )
            return

        if not indice:
            QMessageBox.warning(
                self.dialog,
                tr("Erreur"),
                tr("Le champ 'Indice' est obligatoire.")
            )
            return

        # Reconstruire parc_id ici avec la valeur actuelle de l'indice (et numéro formaté si besoin)
        numero_id = str(self.numero).zfill(4)  # ou adapte selon ta logique de padding
        # Construire l'ID complet
        zero = "0" if len(self.section) == 1 else ""
        parc_id = f"{self.commune}{self.prefixe}{zero}{self.section}{numero_id}{indice}"

        # Sauvegarde valeur combobox type plantation
        typeParc = self.ui.combo_plantation.currentData()

        # Appel de mise à jour avec contrôle du retour
        if not self.update_feature_attributes(section, numero, indice, parc_id, possession, typeParc=typeParc):
            return  # ❌ L'erreur a déjà été affichée dans update_feature_attributes

        QMessageBox.information(
            self.dialog,
            tr("Succès"),
            tr("Les informations ont été sauvegardées.")
        )
        self.dialog.accept()  # Ferme la boîte de dialogue


    # Suppression de l'entité créée
    def delete_feature_by_fid(self, fid):
        if self.layer:
            if not self.layer.isEditable():
                self.layer.startEditing()
            self.layer.deleteFeature(fid)
            self.layer.triggerRepaint()
            print(f"🗑️ Entité {fid} supprimée suite à annulation ou erreur.")

    def on_dialog_rejected(self):
        print("🔁 [on_dialog_rejected] Appelé")
        # Supprime l'entité si la boîte de dialogue est fermée sans validation
        if hasattr(self, "fid") and self.fid is not None:
            self.delete_feature_by_fid(self.fid)
            print(f"🗑️ Entité avec fid {self.fid} supprimée suite à annulation.")

        # Restaure la géométrie de la parcelle mère si on a les infos
        if hasattr(self, "parent_fid") and hasattr(self, "original_parent_geom"):
            if not self.layer.isEditable():
                self.layer.startEditing()
            success = self.layer.changeGeometry(self.parent_fid, self.original_parent_geom)
            if success:
                print(f"♻️ Géométrie de la parcelle mère (fid {self.parent_fid}) restaurée.")
            else:
                print(f"❌ Échec restauration géométrie mère (fid {self.parent_fid}).")

            self.layer.commitChanges()

    def cancel_polygon_creation(self):
        if hasattr(self, "fid") and self.fid is not None:
            self.delete_feature_by_fid(self.fid)
        self.dialog.reject()

    def supprimer_entite_fille(self, fid):
        print(f"[DEBUG] -> Méthode supprimer_entite_fille appelée avec fid = {fid}")

        if fid is None:
            iface.messageBar().pushWarning(tr("Suppression"), tr("Aucune entité sélectionnée."))
            return

        layer = iface.activeLayer()
        if not layer:
            iface.messageBar().pushWarning(tr("Suppression"), tr("Aucune couche active."))
            return

        feature_fille = layer.getFeature(fid)
        if not feature_fille or not feature_fille.isValid():
            iface.messageBar().pushWarning(tr("Suppression"), tr("Entité invalide ou introuvable."))
            return

        if 'indice_parc' not in feature_fille.fields().names() or not feature_fille['indice_parc']:
            iface.messageBar().pushWarning(
                tr("Suppression"), tr("L'entité sélectionnée ne possède pas de champ 'indice_parc'.")
            )
            return

        section = feature_fille['section'] if 'section' in feature_fille.fields().names() else "?"
        numero = feature_fille['numero'] if 'numero' in feature_fille.fields().names() else "?"
        indice = feature_fille['indice_parc'] if 'indice_parc' in feature_fille.fields().names() else "?"

        message = tr(
            "La parcelle <b>{section}</b><b>{numero}</b><b>{indice}</b> sera définitivement supprimée.<br>Souhaitez-vous continuer ?"
        ).format(section=section, numero=numero, indice=indice)

        reply = QMessageBox.question(
            None, tr("Confirmation de suppression"), message, MB_Yes | MB_No, MB_No
        )
        if reply != MB_Yes:
            return

        geom_fille = feature_fille.geometry()
        fille_exterior_ring = (
            geom_fille.asMultiPolygon()[0][0] if geom_fille.isMultipart() else geom_fille.asPolygon()[0]
        )

        parent_found = False
        parent_fid = None
        new_geom = None

        for feature_mere in layer.getFeatures():
            if feature_mere.id() == fid:
                continue

            geom_mere = feature_mere.geometry()

            if geom_mere.isMultipart():
                multi_polygons = geom_mere.asMultiPolygon()
                new_multi_polygons = []

                for polygon in multi_polygons:
                    exterior, interior = polygon[0], polygon[1:]
                    updated_rings = [r for r in interior if r != fille_exterior_ring]
                    if len(interior) != len(updated_rings):
                        parent_found = True
                        parent_fid = feature_mere.id()
                    new_multi_polygons.append([exterior] + updated_rings)

                if parent_found:
                    new_geom = QgsGeometry.fromMultiPolygonXY(new_multi_polygons)
                    break

            else:
                polygon = geom_mere.asPolygon()
                if not polygon:
                    continue

                exterior, interior = polygon[0], polygon[1:]
                updated_rings = [r for r in interior if r != fille_exterior_ring]
                if len(interior) != len(updated_rings):
                    parent_found = True
                    parent_fid = feature_mere.id()
                    new_geom = QgsGeometry.fromPolygonXY([exterior] + updated_rings)
                    break

        print(f"[DEBUG] couche : {layer.name()}, mode édition : {layer.isEditable()}")
        print(f"[DEBUG] ID à supprimer : {fid}")
        print(f"[DEBUG] Couche contient {layer.featureCount()} entités avant suppression")

        was_editing = layer.isEditable()
        if not was_editing:
            print("[DEBUG] -> La couche n'était pas en édition, démarrage forcé.")
            layer.startEditing()

        # --- 🔧 Appliquer la géométrie mise à jour à la parcelle mère ---
        if parent_found and parent_fid and new_geom:
            print(f"[DEBUG] -> Mise à jour géométrie mère (fid {parent_fid}) après suppression trou")
            res_geom = layer.changeGeometry(parent_fid, new_geom)
            print(f"[DEBUG] -> Résultat changeGeometry : {res_geom}")
            layer.updateExtents()
            layer.triggerRepaint()

        # --- 🗑️ Suppression de l'entité fille ---
        res = layer.deleteFeature(fid)
        print(f"[DEBUG] -> Résultat deleteFeature : {res}")

        # --- 💾 Validation si édition locale ---
        if not was_editing:
            print("[DEBUG] -> Commit des modifications.")
            layer.commitChanges()

        layer.triggerRepaint()

        print(f"[DEBUG] Couche contient {layer.featureCount()} entités après suppression")

        if parent_found:
            iface.messageBar().pushInfo(tr("Suppression"), tr("Entité fille et trou supprimés."))
        else:
            iface.messageBar().pushInfo(tr("Suppression"), tr("Entité fille supprimée (pas de trou à corriger)."))

    def update_feature_attributes(self, section, numero, indice, parc_id, possession, typeParc=None):
        """Met à jour les attributs de l'entité avec les valeurs saisies."""
        try:
            fid = int(self.ui.lineFid.text())
        except ValueError:
            QMessageBox.warning(
                self.dialog,
                tr("Erreur"),
                tr("Fid invalide.")
            )
            return False

        if not self.layer or not self.layer.isEditable():
            self.layer.startEditing()

        feature_request = QgsFeatureRequest(fid)
        feature = next(self.layer.getFeatures(feature_request), None)

        if feature is None:
            QMessageBox.warning(
                self.dialog,
                tr("Erreur"),
                tr("Entité {fid} introuvable.").format(fid=fid)
            )
            return False

        # Vérification d'unicité de l'ID
        expr = (
            f'"id" = \'{parc_id}\' AND '
            f'"indice_parc" IS NOT NULL AND '
                f'"fid" != {fid}'
        )
        request = QgsFeatureRequest().setFilterExpression(expr)
        if any(self.layer.getFeatures(request)):
            QMessageBox.warning(
                self.dialog,
                tr("Doublon détecté"),
                tr("L'identifiant '{parc_id}' existe déjà pour une autre entité.\n"
                   "Merci de choisir un autre 'indice_parc'.").format(parc_id=parc_id)
            )
            return False

        # Mise à jour des champs
        feature['section'] = section
        feature['numero'] = numero
        feature['indice_parc'] = indice
        feature['id'] = parc_id
        feature['Possession'] = possession

        # Mise à jour type plantation
        if typeParc is not None and 'typeParc' in self.layer.fields().names():
            feature['typeParc'] = typeParc

        if 'contenance' in self.layer.fields().names():
            feature['contenance'] = round(self.surface_m2, 0)
        if 'SURFACE' in self.layer.fields().names():
            surface_ares = round(self.surface_m2 / 100, 2)
            feature['SURFACE'] = surface_ares

        # Appliquer les modifications
        self.layer.updateFeature(feature)
        self.layer.triggerRepaint()

        return True  # ✅ fin normale, succès