from qgis.PyQt.QtWidgets import (
    QDialog,
    QMessageBox,
    QPushButton,
    QTextEdit,
    QVBoxLayout
)
from .group_polygon_dialog import Ui_Dialog_group
from .group_polygon import GroupPolygonTool
from qgis.PyQt.QtCore import Qt, QDate
from qgis.PyQt.QtGui import QAction

from qgis.core import (
    QgsGeometry,
    QgsFeature,
    QgsFeatureRequest
)

from .utils import tr

from .compat_qt import Window, exec_dialog


class DialogGroup(QDialog):
    def __init__(self, iface, layer, parent=None):
        super().__init__(parent)

        self.setWindowFlags(self.windowFlags() | Window) # Rend la fenêtre indépendante
    #    self.setWindowFlags(self.windowFlags() | Qt.Window) idem dessus ligne qgis4
        self.iface = iface
        self.layer = layer

        # Installer l'UI
        self.ui = Ui_Dialog_group()
        self.ui.setupUi(self)

        # MapTool pour sélectionner les parcelles
        self.group_tool = GroupPolygonTool(self.iface.mapCanvas(), layer)

        # 🔹 On passe une référence à la dialog au MapTool
        self.group_tool.dialog = self

        self.group_tool.group_finished.connect(self.on_group_tool_finished)
        self.group_tool.group_canceled.connect(self.on_group_tool_canceled)

        # Active le MapTool
        self.iface.mapCanvas().setMapTool(self.group_tool)

        # 🔒 Désactivation initiale
        self.set_validation_enabled(False)

        # Connexions boutons
        self.ui.Btn_fin_Rgp.clicked.connect(self.finish_selection)
        self.ui.Rgp_Annule.clicked.connect(self.cancel_group)
        self.ui.Rgp_valide.clicked.connect(self.validate_group)
        self.ui.toolButtonInfoRgp.clicked.connect(self.open_info_regroupement)

        # Variables pour stocker résultats
        self.selected_features = []
        self.combined_geometry = None
        self.total_price = 0
        self.total_surface = 0

        # ---------------------------
        # Activation champs
        # ---------------------------

    def set_validation_enabled(self, enabled: bool):
        self.ui.lineEdit_section_Rgp.setEnabled(enabled)
        self.ui.lineEdit_numero_Rgp.setEnabled(enabled)
        self.ui.lineEdit_surface_rgp.setEnabled(enabled)
        self.ui.Rgp_valide.setEnabled(enabled)

        # ---------------------------
        # Signaux du MapTool
        # ---------------------------

    def selected_feature_count(self) -> int:
        """Compte combien de parcelles sont sélectionnées dans le textEdit"""
        text = self.ui.textEdit_list_Rgp.toPlainText().strip()
        if not text:
            return 0
        return len(text.split(" + "))

    def update_finish_button_state(self):
        """Active Btn_fin_Rgp uniquement si ≥ 2 parcelles"""
        self.ui.Btn_fin_Rgp.setEnabled(self.selected_feature_count() >= 2)

    def on_group_tool_finished(self, result):
        """Quand une sélection valide est faite dans le MapTool"""
        current_text = self.ui.textEdit_list_Rgp.toPlainText().strip()

        new_entries = []
        for f in result["features"]:
            section = f["section"] if "section" in f.fields().names() else ""
            numero = f["numero"] if "numero" in f.fields().names() else ""
            entry = f"{section}{numero}"
            if entry not in current_text.split(" + "):
                new_entries.append(entry)

        # Ajouter au texte existant avec séparateur ' + '
        all_entries = current_text.split(" + ") if current_text else []
        all_entries.extend(new_entries)
        self.ui.textEdit_list_Rgp.setText(" + ".join(all_entries))

        # Mettre à jour l'état du bouton terminer
        self.update_finish_button_state()

    def on_group_tool_canceled(self):
        """Sélection invalide (polygone ne touche aucun autre)"""
        QMessageBox.warning(
                self,
                tr("Sélection invalide"),
                tr("Le polygone sélectionné ne touche aucun polygone existant.")
        )

    # ---------------------------
    # Fin sélection / validation
    # ---------------------------
    def finish_selection(self):
        """Fin de sélection et débloque champs"""
        if self.selected_feature_count() < 2:
            QMessageBox.warning(
                self,
                tr("Regroupement"),
                tr("Vous devez sélectionner au moins deux parcelles pour effectuer un regroupement.")
            )
            return

        # Débloquer les champs
        self.set_validation_enabled(True)
        self.ui.lineEdit_section_Rgp.setFocus()

        # 🔹 Copier les features sélectionnées AVANT de vider le MapTool
        self.selected_features = list(self.group_tool.selected_features)

        # Calcul surface totale
        total_surface_m2 = 0
        for f in self.selected_features:
            val = f.attribute('contenance')  # QGIS 4
            if val is None:
                val = 0
            total_surface_m2 += float(val)

        total_surface_ares = total_surface_m2 / 100
        self.ui.lineEdit_surface_rgp.setText(f"{total_surface_ares:.2f}")

        # Désactiver le MapTool ensuite
        self.group_tool.deactivate()
        self.iface.mapCanvas().unsetMapTool(self.group_tool)

    def cancel_group(self):
        """Annuler la sélection et fermer la dialog"""
        # Si le MapTool est actif, le désactiver
        if hasattr(self, 'group_tool') and self.group_tool:
            self.group_tool.cancel(user_initiated=True)  # annule sans déclencher le warning
            self.group_tool.deactivate()  # désactive l'outil de sélection

        self.reject()  # ferme la dialog avec le code Rejected

    def parcelle_exists(self, section, numero):
        layer = self.layer
        expr = f"\"section\" = '{section}' AND \"numero\" = '{numero}'"

        for f in layer.getFeatures(QgsFeatureRequest().setFilterExpression(expr)):
            return True

        return False

    def open_info_regroupement(self):
        dialog = QDialog()
        dialog.setWindowTitle(
            tr("Informations sur le regroupement")
        )

        layout = QVBoxLayout(dialog)

        text = QTextEdit()
        text.setReadOnly(True)

        # 👉 Tu rempliras toi-même ce texte
        text.setText(self.build_regroupement_info())

        layout.addWidget(text)

        btn = QPushButton("Fermer")
        btn.clicked.connect(dialog.close)
        layout.addWidget(btn)

        exec_dialog(dialog)
        #dialog.exec_() valable uniquement qgis4 qt6

    # Informations sur fonctionnement regroupement
    def build_regroupement_info(self):
        return (
                tr("Attention lors d'un regroupement vous perdrez certaines informations :\n")
                + tr("  • Le type de parcelle sera celui de la plus grande parcelle regroupée.\n")
                + tr("LEs informations financières devront être ressaisies ou vous devrez modifier le numéro de parcelles dans le fichier fin_xxx.csv")
                + tr("  • Pour les travaux et traitements seuls les 6 plus récents seront conservés.\n")
                + tr(
            "  • Les remarques sur le terrain seront remplacées par l'information des anciennes parcelles.\n")
                + tr("  • La population des anciennes parcelles sera recalculée aux 4 essences les plus importantes.\n")
                + tr("  • Les parcelles filles seront conservées et attribuées au nouveau numéro avec toutes leurs anciennes informations.\n")
                + tr("  • Le nombre de plants et les prix des anciennes parcelles seront additionnés.\n")
                + tr("  • La date d'achat conservée sera la date la plus ancienne.\n")
        )

    def compute_regrouped_species(self, feats):
        """
        Calcule les 4 essences dominantes pour une parcelle regroupée,
        pondérées par la surface des anciennes parcelles.
        Retourne :
            - une liste (max 4) des essences classées
            - une liste (max 4) des pourcentages correspondants
        """

        species_weights = {}  # { "Chêne" : poids total, "Hêtre" : poids total, ... }

        for f in feats:
            geom = f.geometry()
            if geom is None:
                continue

            surface = geom.area()
            if surface <= 0:
                continue

            # ---- On lit plant1–4 et Tx1–4 ----
            for i in range(1, 5):
                plant = f[f"plant{i}"]
                tx = f[f"Tx{i}"]

                if plant and tx and tx > 0:
                    weight = (tx / 100.0) * surface  # contribution réelle d’essence sur la surface
                    species_weights[plant] = species_weights.get(plant, 0) + weight

        if not species_weights:
            return [], []

        # ---- Classement des essences par poids décroissant ----
        sorted_species = sorted(species_weights.items(), key=lambda x: x[1], reverse=True)

        # ---- Sélection des 4 plus importantes ----
        top = sorted_species[:4]

        species = [sp for sp, w in top]
        weights = [w for sp, w in top]

        # ---- Normalisation en pourcentage (somme = 100) ----
        total = sum(weights)
        if total > 0:
            percentages = [round((w / total) * 100) for w in weights]
        else:
            percentages = [0 for w in weights]

        # Ajustement si somme != 100 à cause des arrondis
        diff = 100 - sum(percentages)
        if diff != 0 and percentages:
            percentages[0] += diff

        return species, percentages

    # Gestion des informations travaux et traitements pour le regroupement
    def compute_regrouped_operations(self, feats):
        """
        Regroupe :
          - Tvx1..6 : tri par date → garder les plus récentes (max 6)
          - Trait1..6 : tri par date → garder les plus récentes (max 6)
          - Prev1..4 : priorité aux grandes parcelles → garder celles des grandes parcelles (max 4)
        """

        # ---- Fonction interne de normalisation des dates ----
        def norm_date(d):
            """Convertit une valeur en QDate fiable."""
            if isinstance(d, QDate):
                return d
            if not d:
                return QDate(1900, 1, 1)
            q = QDate.fromString(str(d), "dd-MM-yyyy")
            return q if q.isValid() else QDate(1900, 1, 1)

        # --------------------
        # Extraction brute
        # --------------------
        tvx_list = []
        trait_list = []
        prev_list = []

        for f in feats:
            surface = float(f.geometry().area() or 0)

            # TVX
            for i in range(1, 7):
                tv = f[f"Tvx{i}"]
                d = f[f"dateTvx{i}"]
                rem = f[f"remTvx{i}"]
                if tv:
                    tvx_list.append({
                        "code": tv,
                        "date": norm_date(d),
                        "rem": rem
                    })

            # TRAIT
            for i in range(1, 7):
                tr = f[f"Trait{i}"]
                d = f[f"dateTrait{i}"]
                rem = f[f"remTrait{i}"]
                if tr:
                    trait_list.append({
                        "code": tr,
                        "date": norm_date(d),
                        "rem": rem
                    })

            # PREV → spécial : date non date, mais sélection selon surface
            for i in range(1, 5):
                pr = f[f"Prev{i}"]
                d = f[f"datePrev{i}"]  # texte
                rem = f[f"remPrev{i}"]
                if pr:
                    prev_list.append({
                        "code": pr,
                        "date": d,
                        "rem": rem,
                        "surface": surface
                    })

        # --------------------
        # 📌 TVX : tri par date décroissante
        # --------------------
        tvx_list = sorted(tvx_list, key=lambda x: x["date"], reverse=True)[:6]

        # --------------------
        # 📌 TRAIT : tri par date décroissante
        # --------------------
        trait_list = sorted(trait_list, key=lambda x: x["date"], reverse=True)[:6]

        # --------------------
        # 📌 PREV : priorité → grande surface
        # --------------------
        prev_list = sorted(prev_list, key=lambda x: x["surface"], reverse=True)[:4]

        return {
            "tvx": tvx_list,
            "trait": trait_list,
            "prev": prev_list
        }

    def validate_group(self):
        """Bouton Rgp_valide cliqué → créer nouvelle parcelle"""

        layer = self.layer
        feats = self.selected_features

        if not feats or len(feats) < 2:
            QMessageBox.warning(
                self,
                tr("Regroupement"),
                tr("Aucune parcelle à regrouper.")
            )
            return

        section = self.ui.lineEdit_section_Rgp.text().strip()
        numero = self.ui.lineEdit_numero_Rgp.text().strip()

        if not section or not numero:
            QMessageBox.warning(
                self,
                tr("Champs manquants"),
                tr("Section et numéro sont obligatoires.")
            )
            return

        # 🔒 Vérification unicité section + numero
        if self.parcelle_exists(section, numero):
            QMessageBox.critical(
                self,
                tr("Doublon"),
                tr("La parcelle {section}{numero} existe déjà.").format(
                    section=section,
                    numero=numero
                )
            )
            return

        # ---------------------------
        # Calculs
        # ---------------------------

        geom = None
        contenance_m2 = 0
        prix = 0
        prixAct = 0
        totalplants = 0
        dates = []
        typeParcs = set()

        for f in feats:
            # Géométrie (union)
            if geom is None:
                geom = QgsGeometry(f.geometry())
            else:
                geom = geom.combine(f.geometry())

            contenance_m2 += int(f["contenance"] or 0)
            prix += float(f["prix"] or 0)
            prixAct += float(f["prixAct"] or 0)
            totalplants += int(f["totalplants"] or 0)

            if f["dateAchat"]:
                dates.append(f["dateAchat"])

            if f["typeParc"]:
                typeParcs.add(f["typeParc"])

        # ---------------------------
        # Surface et infos attributaires
        # ---------------------------
        surface_ares = round(contenance_m2 / 100, 2)
        dateAchat = min(dates) if dates else None
        typeParc = typeParcs.pop() if len(typeParcs) == 1 else None

        # ---------------------------
        # ❗❗ CONSERVATION DES TROUS (entités filles)
        # ---------------------------

        # Couples (section, numero) des entités regroupées
        selected_pairs = {(f["section"], f["numero"]) for f in feats}

        # Recherche des entités filles dans la couche
        daughter_feats = []
        for f2 in layer.getFeatures():
            if (f2["section"], f2["numero"]) in selected_pairs:
                if f2["indice_parc"] not in (None, "", 0):
                    daughter_feats.append(f2)

        # Soustraction géométrique → conservation des trous
        for df in daughter_feats:
            geom = geom.difference(df.geometry())

        # ---------------------------
        # 📌 Détermination du typeParc pour la nouvelle parcelle
        # ---------------------------

        # On ne considère que les entités mères (indice_parc vide ou None)
        mother_feats = [f for f in feats if not f["indice_parc"]]

        if mother_feats:
            # Trouver la parcelle mère avec la plus grande contenance
            largest_feat = max(mother_feats, key=lambda f: int(f["contenance"] or 0))
            typeParc = largest_feat["typeParc"]
        else:
            typeParc = None  # sécurité, au cas où

        # ---------------------------
        # 📌 Détermination du champ annee (integer)
        # ---------------------------
        annees = []

        for f in feats:
            an = f["annee"]
            if an and isinstance(an, int):
                annees.append(an)

        if annees:
            annee = min(annees)  # Plus ancienne année
        else:
            annee = None

        # ---------------------------------------------------------
        # 📌 Mise à jour des entités filles avant regroupement
        # ---------------------------------------------------------

        # Nouveau numero choisi par l'utilisateur
        new_numero = numero

        # Construction section_str + zero
        section_str = str(section)
        zero = "0" if len(section_str) == 1 else ""

        # Réindexation propre des filles → recommence toujours à "a"
        import string
        alphabet = list(string.ascii_lowercase)

        # Trier pour stabilité
        daughter_feats_sorted = sorted(daughter_feats, key=lambda f: f.id())

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

        for i, f in enumerate(daughter_feats_sorted):
            # Nouvel indice (a, b, c…)
            new_indice = alphabet[i]

            # Mise à jour du champ numero
            f["numero"] = new_numero

            # Mise à jour indice_parc
            f["indice_parc"] = new_indice

            # Recalcule de l'ID complet
            commune = f["commune"]
            prefixe = f["prefixe"]
            numero_id = str(new_numero).zfill(4)

            new_id = f"{commune}{prefixe}{zero}{section_str}{numero_id}{new_indice}"
            f["id"] = new_id

            layer.updateFeature(f)

        # ---------------------------
        # 📌 Texte pour RemTerrain (anciennes références)
        # ---------------------------
        anciennes_refs = []

        for f in feats:
            sec = f["section"] or ""
            num = f["numero"] or ""

            ref = f"{sec}{num}"
            anciennes_refs.append(ref)

        remterrain_txt = tr("Anciennes parcelles ") + ", ".join(anciennes_refs)

        # ---------------------------
        # 📌 Détermination de dateAchat
        # ---------------------------
        dates = []

        for f in feats:
            d = f["dateAchat"]

            if not d:
                continue

            # Si le champ est déjà un QDate → OK
            if isinstance(d, QDate):
                dates.append(d)
                continue

            # Si le champ arrive comme string → conversion
            qd = QDate.fromString(str(d), "dd-MM-yyyy")
            if qd.isValid():
                dates.append(qd)

        if dates:
            dateAchat = min(dates)
        else:
            dateAchat = QDate.currentDate()

        # ---------------------------
        # Construction de l'ID unique
        # ---------------------------

        ref = feats[0]

        commune = ref["commune"]
        prefixe = ref["prefixe"]

        indice_parc = ref["indice_parc"] if ref["indice_parc"] else ""

        numero_id = str(numero).zfill(4)
        section_str = str(section)
        zero = "0" if len(section_str) == 1 else ""

        parc_id = f"{commune}{prefixe}{zero}{section_str}{numero_id}{indice_parc}"

        # ---------------------------
        # Nouvelle entité
        # ---------------------------

        new_feat = QgsFeature(layer.fields())
        new_feat.setGeometry(geom)

        new_feat["section"] = section
        new_feat["numero"] = numero
        new_feat["contenance"] = contenance_m2
        new_feat["SURFACE"] = surface_ares
        new_feat["prix"] = prix
        new_feat["prixAct"] = prixAct
        new_feat["dateAchat"] = dateAchat
        new_feat["possession"] = 1
        new_feat["typeParc"] = typeParc
        new_feat["totalplants"] = totalplants
        new_feat["id"] = parc_id
        new_feat["commune"] = commune
        new_feat["prefixe"] = prefixe
        new_feat["typeParc"] = typeParc
        new_feat["RemTerrain"] = remterrain_txt
        new_feat["annee"] = annee

        # ---------------------------
        # 🌲 Mise à jour des essences → plant1..4 et Tx1..4
        # ---------------------------

        # Appel à ta fonction de calcul
        species, percentages = self.compute_regrouped_species(feats)

        # Nettoyage préalable des champs
        for i in range(1, 5):
            new_feat[f"plant{i}"] = None
            new_feat[f"Tx{i}"] = None

        # Remplissage avec les valeurs calculées
        for i, (sp, tx) in enumerate(zip(species, percentages), start=1):
            if i > 4:
                break
            new_feat[f"plant{i}"] = sp
            new_feat[f"Tx{i}"] = tx

        # ---------------------------
        # Regroupement des TVX / TRAIT / PREV
        # ---------------------------
        ops = self.compute_regrouped_operations(feats)

        # Nettoyage préalable
        for i in range(1, 7):
            new_feat[f"Tvx{i}"] = None
            new_feat[f"dateTvx{i}"] = None
            new_feat[f"remTvx{i}"] = None

            new_feat[f"Trait{i}"] = None
            new_feat[f"dateTrait{i}"] = None
            new_feat[f"remTrait{i}"] = None

        for i in range(1, 5):
            new_feat[f"Prev{i}"] = None
            new_feat[f"datePrev{i}"] = None
            new_feat[f"remPrev{i}"] = None

        # Écriture TVX
        for i, d in enumerate(ops["tvx"], start=1):
            new_feat[f"Tvx{i}"] = d["code"]
            new_feat[f"dateTvx{i}"] = d["date"]
            new_feat[f"remTvx{i}"] = d["rem"]

        # Écriture TRAIT
        for i, d in enumerate(ops["trait"], start=1):
            new_feat[f"Trait{i}"] = d["code"]
            new_feat[f"dateTrait{i}"] = d["date"]
            new_feat[f"remTrait{i}"] = d["rem"]

        # Écriture PREV
        for i, d in enumerate(ops["prev"], start=1):
            new_feat[f"Prev{i}"] = d["code"]
            new_feat[f"datePrev{i}"] = d["date"]
            new_feat[f"remPrev{i}"] = d["rem"]

        # ---------------------------
        # Écriture couche
        # ---------------------------

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

        layer.addFeature(new_feat)
        layer.deleteFeatures([f.id() for f in feats])

        if not layer.commitChanges():
            layer.rollBack()
            QMessageBox.critical(
                self,
                tr("Erreur"),
                tr("Erreur lors de l’enregistrement.")
            )
            return

        layer.triggerRepaint()
        self.accept()

