from qgis.PyQt.QtCore import QObject, pyqtSignal, Qt
from qgis.gui import QgsMapToolEdit, QgsRubberBand
from qgis.core import (
    Qgis,
    QgsGeometry,
    QgsPointXY,
    QgsFeature,
    QgsProject,
    QgsFeatureRequest,
    QgsSnappingUtils,
    QgsGeometryUtils,
)
from qgis.PyQt.QtGui import QCursor,QPixmap

from qgis.PyQt.QtWidgets import QDialog

import os

from .infos_polygon import InfosPolygonManager
from .create_polygon_dialog import Ui_createPolygonDialog

from .compat_qt import (
     LeftButton,
     RightButton,
     ColorRed,
     Key_Return,
     Key_Enter,
     Key_Escape,
     exec_dialog,
     CrossCursor,
)

# -------------------------------
#  Classe dédiée uniquement aux signaux
# -------------------------------
class FillRingSignals(QObject):
    ring_created = pyqtSignal(QgsGeometry) # Signal émis à la fin du dessin, avec la géométrie du trou



class FillRingTool(QgsMapToolEdit):


    def __init__(self, canvas, layer, callback=None):
        super().__init__(canvas)  # Appelle le constructeur de QgsMapToolEdit avec canvas
        self.canvas = canvas
        self.layer = layer
        self.callback = callback

        # Création de l'objet contenant les signaux Qt
        self.signals = FillRingSignals()

        # Connecte le callback si fourni
        if self.callback:
            self.signals.ring_created.connect(self.callback)

        self.points = []
        self.rubber_band = QgsRubberBand(self.canvas, Qgis.GeometryType.Polygon)
        self.rubber_band.setColor(ColorRed)
        self.rubber_band.setWidth(2)

        # ✅ Déclaration des attributs
        self.original_parent_geom = None
        self.parent_fid = None


    def activate(self):
        super().activate()

        plugin_dir = os.path.dirname(__file__)
        cursor_path = os.path.join(plugin_dir, "icons", "cursor_ringfill.png")
        if os.path.exists(cursor_path):
            pixmap = QPixmap(cursor_path)
            cursor = QCursor(pixmap, 16, 16)  # (hotspot_x, hotspot_y)
            self.canvas.setCursor(cursor)
        else:
            print("❌ Curseur personnalisé non trouvé :", cursor_path)


    def deactivate(self):
        super().deactivate()
        self.canvas.unsetCursor()

    def canvasPressEvent(self, event):
        """Gestion du clic sur le canvas"""

        point = self.toMapCoordinates(event.pos())

        # --- Détection de la géométrie parente ---
        if self.original_parent_geom is None:
            for f in self.layer.getFeatures():
                if f.geometry().contains(point):
                    self.original_parent_geom = QgsGeometry(f.geometry())
                    self.parent_fid = f.id()
                    break

        # --- Snap éventuel sur les sommets ---
        if self.original_parent_geom is not None:
            snap_tolerance = 10 * self.canvas.mapUnitsPerPixel()
            closest_point = None
            min_dist = float('inf')

            for vertex in self.original_parent_geom.vertices():
                v = QgsPointXY(vertex)
                dist = point.distance(v)
                if dist < min_dist and dist <= snap_tolerance:
                    min_dist = dist
                    closest_point = v

            if closest_point is not None:
                point = closest_point

        # --- Gestion clic gauche / droit ---
        if event.button() == LeftButton:
            self.points.append(point)
            self.rubber_band.addPoint(point, True)
            self.rubber_band.show()

        elif event.button() == RightButton:
            self.finish_polygon()

    def canvasMoveEvent(self, event):
        """Affichage dynamique du rubber band et curseur"""
        point = self.toMapCoordinates(event.pos())


        # Déplacer le dernier point du rubber band
        if self.points:
            self.rubber_band.movePoint(point)
            self.rubber_band.show()

        # --- Indicateur de snap sur sommet ---
        if self.original_parent_geom is not None:
            snap_tolerance_px = 10
            map_units_per_pixel = self.canvas.mapUnitsPerPixel()
            snap_tolerance = snap_tolerance_px * map_units_per_pixel

            snap_on_vertex = False

            for vertex in self.original_parent_geom.vertices():
                vertex_point = QgsPointXY(vertex)
                if point.distance(vertex_point) <= snap_tolerance:
                    snap_on_vertex = True
                    break

            # Changer le curseur selon qu'on est sur un sommet ou pas
            if snap_on_vertex:
                self.canvas.setCursor(CrossCursor)
            else:
                # Curseur personnalisé défini dans activate()
                plugin_dir = os.path.dirname(__file__)
                cursor_path = os.path.join(plugin_dir, "icons", "cursor_ringfill.png")
                if os.path.exists(cursor_path):
                    pixmap = QPixmap(cursor_path)
                    cursor = QCursor(pixmap, 16, 16)
                    self.canvas.setCursor(cursor)

    def canvasReleaseEvent(self, event):
        # Rien à faire au relâchement, on valide au double-clic

        pass

    def keyPressEvent(self, event):
        # Valider le polygone au double-clic ou touche Entrée
        key = event.key()
        if key == Key_Return or key == Key_Enter:
            self.finish_polygon()
        elif key == Key_Escape:
            self.cancel()

    def finish_polygon(self):
        if len(self.points) < 3:
            self.cancel()
            return

        self.rubber_band.closePoints()
        ring_geom = QgsGeometry.fromPolygonXY([self.points])

        # Trouver la parcelle mère
        parent_feature = None
        for f in self.layer.getFeatures():
            if f.geometry().contains(ring_geom.centroid().asPoint()):
                parent_feature = f
                break

        if not parent_feature:
            self.cancel()
            return

        parent_geom = QgsGeometry(parent_feature.geometry())

        # ---------------------------------------------------------
        # 🔁 RECONSTRUCTION SYSTÉMATIQUE DE LA GÉOMÉTRIE MÈRE + TROUS EXISTANTS
        # ---------------------------------------------------------
        rebuilt_geom = None

        if parent_geom.isMultipart():
            # Multi → on reconstruit proprement sans rien perdre
            multi = parent_geom.asMultiPolygon()
            ring_point = ring_geom.centroid().asPoint()

            # Trouver quel polygone doit recevoir le trou
            target_index = None
            for i, poly in enumerate(multi):
                ext = QgsGeometry.fromPolygonXY([poly[0]])
                if ext.contains(ring_point):
                    target_index = i
                    break

            if target_index is None:
                print("❌ Aucun polygone ne contient le trou.")
                self.cancel()
                return

            # Ajout du trou dans la liste des anneaux internes
            multi[target_index].append(self.points)
            rebuilt_geom = QgsGeometry.fromMultiPolygonXY(multi)

        else:
            # Polygone simple → on reconstruit la géométrie complète à partir de l’existant
            simple = parent_geom.asPolygon()

            if not simple:
                print("❌ Polygone invalide.")
                self.cancel()
                return

            # simple[0] = anneau extérieur
            # simple[1:] = trous existants

            new_poly = [simple[0]] + simple[1:]  # copie complète
            new_poly.append(self.points)  # ajout du nouveau trou
            rebuilt_geom = QgsGeometry.fromPolygonXY(new_poly)

        # ---------------------------------------------------------
        # 💾 Mise à jour de la géométrie mère
        # ---------------------------------------------------------
        if not self.layer.isEditable():
            self.layer.startEditing()

        res = self.layer.changeGeometry(parent_feature.id(), rebuilt_geom)

        if not res:
            print("❌ Impossible de modifier la géométrie mère.")
            self.cancel()
            return

        # ---------------------------------------------------------
        # ➕ Ajout de l'entité fille (trou)
        # ---------------------------------------------------------
        self.layer.updateExtents()
        self.layer.triggerRepaint()

        success_commit = self.layer.commitChanges()
        if not success_commit:
            print("❌ Échec du commit mère.")

        # Ajout
        new_feat = QgsFeature(self.layer.fields())
        new_feat.setGeometry(ring_geom)
        ok, added = self.layer.dataProvider().addFeatures([new_feat])

        if not ok or not added:
            print("❌ Impossible d'ajouter l'entité trou.")
            self.cancel()
            return

        fid = added[0].id()
        inserted_feature = next(self.layer.getFeatures(QgsFeatureRequest(fid)))

        # ---------------------------------------------------------
        # 📌 OUVERTURE DU DIALOGUE
        # ---------------------------------------------------------
        dialog = QDialog()
        ui = Ui_createPolygonDialog()
        ui.setupUi(dialog)

        manager = InfosPolygonManager(ui, dialog)
        manager.fid = fid

        # parent_feature déjà identifié
        manager.parent_fid = parent_feature.id()
        manager.original_parent_geom = parent_geom

        manager.open_dialog_from_geometry(inserted_feature.geometry(), self.layer, fid)
        dialog.rejected.connect(manager.on_dialog_rejected)

        exec_dialog(dialog)

        # Nettoyage
        self.points = []
        self.rubber_band.reset(Qgis.GeometryType.Polygon)

    def cancel(self):
        print("⛔ Annulation du dessin (Esc)")
        self.points = []
        self.rubber_band.reset(Qgis.GeometryType.Polygon)
        self.canvas.refresh()  # Facultatif mais utile pour forcer le rafraîchissement


