# -*- coding: utf-8 -*-
"""
/***************************************************************************
 QUtilityDialog
                                 A QGIS plugin
 Un toolbox multifunzione per QGIS
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2025-05-06
        git sha              : $Format:%H$
        copyright            : (C) 2025 by Lorenzo Alunni
        email                : gis@eagleprojects.it
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os
import sys
import processing
import time
from qgis.PyQt import QtGui, QtWidgets, uic # type: ignore
from qgis.PyQt.QtCore import Qt # type: ignore
from qgis.core import (QgsProject, QgsVectorLayer, QgsVectorFileWriter, QgsField,
                       QgsMessageLog, Qgis, QgsMapLayer, QgsWkbTypes, QgsCoordinateReferenceSystem, QgsCoordinateTransform,
                       QgsProcessingContext, QgsProcessingFeedback, QgsExpression, QgsExpressionContext, QgsExpressionContextUtils,QgsMapLayerProxyModel)
from qgis.gui import QgsFileWidget
from qgis.PyQt.QtWidgets import QApplication, QMessageBox # type: ignore
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QVariant, QRegExp
from .layer_renamer import LayerRenamer
from .layer_loader import LayerLoader
from .feature_excluder import FeatureExcluder
from .batch_converter import BatchConverter

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'qutility_dialog_base.ui'))


class QUtilityDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(QUtilityDialog, self).__init__(parent)
        # Set up the user interface from Designer
        self.setupUi(self)
        
        # Connetti i segnali
        self.fmlayerlist.itemSelectionChanged.connect(self.update_selection_count)
        self.fmlayerlist.itemSelectionChanged.connect(self.update_fields)
        self.fmrun.clicked.connect(self.modify_field)
        self.fmtype.currentIndexChanged.connect(self.toggle_precision_field)
        
        # Configurazione validatore per il campo fmname (solo caratteri letterali, numeri e underscore)
        regex = QRegExp("[a-zA-Z0-9_]+")
        name_validator = QRegExpValidator(regex, self)
        self.fmname.setValidator(name_validator)
        
        # Inizialmente disattiva il campo nome
        self.fmname.setEnabled(False)
        
        # Aggiungi connessione per attivare/disattivare fmname
        self.fmfield.currentTextChanged.connect(self.toggle_name_field)
        # Inizializza il widget expression e connetti il segnale fieldChanged
        self.field_expression_fm.setLayer(None)
        self.field_expression_fm.fieldChanged.connect(self.on_expression_changed)

        # Disabilita inizialmente il widget expression
        self.field_expression_fm.setEnabled(False)

        # Imposta i validatori per accettare solo numeri
        # Validatore per il campo lunghezza (solo numeri interi positivi)
        int_validator = QtGui.QIntValidator(1, 999, self)
        self.fmlun.setValidator(int_validator)
        
        # Validatore per il campo precisione (solo numeri interi non negativi)
        precision_validator = QtGui.QIntValidator(0, 99, self)
        self.fmpreci.setValidator(precision_validator)

        # Inizializzi i componenti
        self.layer_renamer = LayerRenamer(self)
        self.layer_loader = LayerLoader(self)
        self.feature_excluder = FeatureExcluder(self)
        self.batch_converter = BatchConverter(self)

        # Popola i tipi di campo
        self.populate_field_types()
        
        # Popola la lista dei layer
        self.populate_layers()
        self.layer_renamer.populate_layers()
        
        # Inizialmente disattiva il campo precisione
        self.fmpreci.setEnabled(False)
        
        # Connetti gli eventi di progetto
        QgsProject.instance().layersAdded.connect(self.on_layers_changed)
        QgsProject.instance().layersRemoved.connect(self.on_layers_changed)

        # Inizializzazione Overlaps Counter
        self.setup_overlaps_counter()

        # Inizializzazione Batch Merger
        self.setup_batch_merger()

        # Setup link cliccabili sezione Home
        self.textBrowser_2.setOpenExternalLinks(False)
        self.textBrowser_2.anchorClicked.connect(self.handle_info_link_click)

    def toggle_name_field(self, text):
        """Attiva/disattiva il campo nome e l'expression widget in base alla selezione del campo
        e imposta un placeholder dinamico"""
        is_field_selected = bool(text)
        
        # Attiva/disattiva il campo nome
        self.fmname.setEnabled(is_field_selected)
        
        # Attiva/disattiva il widget expression se c'è un campo selezionato e almeno un layer
        selected_layers = self.get_selected_layers()
        self.field_expression_fm.setEnabled(is_field_selected and len(selected_layers) > 0)
        
        if not is_field_selected:
            # Se nessun campo è selezionato, pulisci il campo e il placeholder
            self.fmname.clear()
            self.fmname.setPlaceholderText("")
            # Non resettare il layer per l'expression widget
        else:
            # Se un campo è selezionato, imposta un placeholder dinamico
            self.fmname.setPlaceholderText(f"Leave blank to keep '{text}'")

    def on_expression_changed(self, field_name):
        """Gestisce il cambio di espressione nel widget"""
        # Questa funzione viene chiamata quando l'utente modifica l'espressione
        pass

    def on_layers_changed(self):
        """Gestisce gli eventi di aggiunta/rimozione layer"""
        self.populate_layers()
        self.layer_renamer.populate_layers()
    
    def populate_layers(self):
        """Popola la lista dei layer vettoriali"""
        self.fmlayerlist.clear()
        
        # Ottieni tutti i layer dal progetto
        layers = QgsProject.instance().mapLayers().values()
        
        # Filtra solo i layer vettoriali
        for layer in layers:
            if isinstance(layer, QgsVectorLayer):
                item = QtWidgets.QListWidgetItem(layer.name())
                item.setData(Qt.UserRole, layer)
                self.fmlayerlist.addItem(item)
    
    def populate_field_types(self):
        """Popola il menu a tendina con i tipi di campo"""
        self.fmtype.clear()
        self.fmtype.addItem("String")
        self.fmtype.addItem("Integer")
        self.fmtype.addItem("Double")
        # self.fmtype.addItem("Date")
    
    def update_fields(self):
        """Aggiorna la lista dei campi"""
        self.fmfield.clear()
        selected_layers = self.get_selected_layers()
        
        if not selected_layers:
            return
        
        # Se c'è un solo layer, mostra tutti i suoi campi
        if len(selected_layers) == 1:
            layer = selected_layers[0]
            for field in layer.fields():
                self.fmfield.addItem(field.name())
        else:
            # Altrimenti mostra solo i campi comuni a tutti i layer
            common_fields = None
            
            for layer in selected_layers:
                fields = set(field.name() for field in layer.fields())
                
                if common_fields is None:
                    common_fields = fields
                else:
                    common_fields = common_fields.intersection(fields)
            
            if common_fields:
                for field_name in sorted(common_fields):
                    self.fmfield.addItem(field_name)

        # Aggiorna il layer per il widget expression
        # Imposta il primo layer selezionato come riferimento per l'expression widget
        # L'espressione sarà poi applicata a tutti i layer selezionati
        selected_layers = self.get_selected_layers()
        if selected_layers:
            self.field_expression_fm.setLayer(selected_layers[0])
            # Mantieni abilitato se c'è un campo selezionato
            current_field = self.fmfield.currentText()
            self.field_expression_fm.setEnabled(bool(current_field))
        else:
            self.field_expression_fm.setLayer(None)
            self.field_expression_fm.setEnabled(False)
    
    def get_selected_layers(self):
        """Restituisce una lista dei layer selezionati"""
        selected_layers = []
        
        for item in self.fmlayerlist.selectedItems():
            layer = item.data(Qt.UserRole)
            if layer:
                selected_layers.append(layer)
        
        return selected_layers
    
    def toggle_precision_field(self):
        """Attiva/disattiva il campo precisione in base al tipo selezionato"""
        # Abilita il campo precisione solo se il tipo è Double
        self.fmpreci.setEnabled(self.fmtype.currentText() == "Double")
        
        # Se non è Double, pulisci il campo
        if self.fmtype.currentText() != "Double":
            self.fmpreci.setText("")

    def update_selection_count(self):
        """Aggiorna il conteggio dei layer selezionati"""
        selected_count = len(self.fmlayerlist.selectedItems())
        if selected_count > 0:
            self.fmrun.setText(f"  Apply Changes to Selected ({selected_count})")
        else:
            self.fmrun.setText("  Apply Changes to Selected")

    def modify_field(self):
        """Modifica il campo selezionato nei layer selezionati"""
        # Ottieni i layer selezionati
        selected_layers = self.get_selected_layers()
        if not selected_layers:
            QMessageBox.warning(self, "Warning", "Select at least one layer")
            return
        
        # Ottieni il campo selezionato
        field_name = self.fmfield.currentText()
        if not field_name:
            QMessageBox.warning(self, "Warning", "Select a field to modify")
            return
        
        # Ottieni il nuovo nome se specificato
        new_field_name = self.fmname.text().strip()

        # Se non è specificato un nuovo nome, usa il nome attuale
        if not new_field_name:
            new_field_name = field_name
        
        # Ottieni il nuovo tipo
        new_type = self.fmtype.currentText()
        
        # Ottieni la lunghezza
        try:
            new_length = int(self.fmlun.text()) if self.fmlun.text() else 255
            if new_length <= 0:
                raise ValueError()
        except ValueError:
            QMessageBox.warning(self, "Error", "Length must be a positive number")
            return
        
        # Ottieni la precisione (solo per Double)
        precision = 0
        if new_type == "Double":
            try:
                precision = int(self.fmpreci.text()) if self.fmpreci.text() else 2
                if precision < 0:
                    raise ValueError()
            except ValueError:
                QMessageBox.warning(self, "Error", "Precision must be a non-negative number")
                return

        # Chiedi conferma
        confirmation_message = f"Modify the field '{field_name}' in the selected layers?\n\n" \
                            "This operation may result in data loss if the conversion is not compatible.\n" \
                            "It is recommended to make a backup before proceeding.\n\n" \
                            f"New type: {new_type}\n" \
                            f"Length: {new_length}"

        if new_type == "Double":
            confirmation_message += f"\nPrecisione: {precision}"

        if new_field_name != field_name:
            confirmation_message += f"\nNuovo nome: '{new_field_name}'"
            
        reply = QMessageBox.question(
            self, 
            "Conferma", 
            confirmation_message,
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        if reply == QMessageBox.No:
            return
        
        # Ottieni la formula/espressione se presente
        expression_text = self.field_expression_fm.expression()
        use_expression = expression_text.strip() != ""
        if use_expression:
            confirmation_message += f"\nFormula da applicare: {expression_text}"
        
        # Disabilita i controlli
        self.fmrun.setEnabled(False)
        self.fmlayerlist.setEnabled(False)
        self.fmfield.setEnabled(False)
        self.fmtype.setEnabled(False)
        self.fmlun.setEnabled(False)
        self.fmpreci.setEnabled(False)
        
        # Crea e mostra il popup di elaborazione
        progress_dialog = QMessageBox(self)
        progress_dialog.setWindowTitle("Processing in progress")
        progress_dialog.setText("Modifying field in progress...\nDo not close QGIS.")
        progress_dialog.setStandardButtons(QMessageBox.NoButton)
        progress_dialog.setIcon(QMessageBox.Information)
        
        # Mostra il popup senza bloccare
        progress_dialog.show()
        QApplication.processEvents()
        
        # Determina il tipo QVariant
        if new_type == "String":
            qvariant_type = QVariant.String
        elif new_type == "Integer":
            qvariant_type = QVariant.Int
        elif new_type == "Double":
            qvariant_type = QVariant.Double
        elif new_type == "Date":
            qvariant_type = QVariant.Date
        else:
            qvariant_type = QVariant.String
        
        error_layers = []
        success_layers = []
        
        # Processa ogni layer
        for layer in selected_layers:
            try:
                # Aggiorna l'UI
                QApplication.processEvents()
                
                # Verifica l'indice del campo
                field_idx = layer.fields().indexFromName(field_name)
                if field_idx == -1:
                    error_layers.append(f"{layer.name()} (campo non trovato)")
                    continue
                
                # Crea un nome temporaneo per il nuovo campo
                temp_field_name = field_name + "_new"
                
                # Avvia la modifica
                if not layer.isEditable():
                    layer.startEditing()
                
                # Crea un nuovo campo con il nuovo tipo
                new_field = QgsField(temp_field_name, qvariant_type, new_type, new_length, precision)
                layer.addAttribute(new_field)
                layer.updateFields()
                
                # Ottieni l'indice del nuovo campo
                new_idx = layer.fields().indexFromName(temp_field_name)
                
                # Copia i valori
                # Determina se usare l'espressione o copiare i valori
                if use_expression:
                    # Usa l'espressione per calcolare i valori
                    expr = QgsExpression(expression_text)
                    context = QgsExpressionContext()
                    context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                    
                    conversion_errors = 0
                    max_errors_to_log = 10
                    
                    for feature in layer.getFeatures():
                        context.setFeature(feature)
                        value = expr.evaluate(context)
                        
                        if expr.hasEvalError():
                            if conversion_errors < max_errors_to_log:
                                QgsMessageLog.logMessage(
                                    f"Errore espressione per feature {feature.id()}: {expr.evalErrorString()}", 
                                    "QUtility", 
                                    Qgis.Warning
                                )
                            conversion_errors += 1
                            layer.changeAttributeValue(feature.id(), new_idx, None)
                            continue
                        
                        try:
                            converted_value = None
                            
                            if value is None or (isinstance(value, QVariant) and value.isNull()):
                                converted_value = None
                            elif qvariant_type == QVariant.Int:
                                if isinstance(value, str):
                                    if value.strip() == "":
                                        converted_value = None
                                    else:
                                        converted_value = int(float(value))
                                else:
                                    converted_value = int(value)
                            elif qvariant_type == QVariant.Double:
                                if isinstance(value, str):
                                    if value.strip() == "":
                                        converted_value = None
                                    else:
                                        converted_value = float(value)
                                else:
                                    converted_value = float(value)
                            elif qvariant_type == QVariant.String:
                                converted_value = str(value) if value is not None else None
                            else:
                                converted_value = value
                            
                            layer.changeAttributeValue(feature.id(), new_idx, converted_value)
                            
                        except (ValueError, TypeError) as e:
                            if conversion_errors < max_errors_to_log:
                                QgsMessageLog.logMessage(
                                    f"Errore conversione per feature {feature.id()}: impossibile convertire '{value}' in {new_type}. Errore: {str(e)}", 
                                    "QUtility", 
                                    Qgis.Warning
                                )
                            conversion_errors += 1
                            layer.changeAttributeValue(feature.id(), new_idx, None)
                    
                    if conversion_errors > 0:
                        QgsMessageLog.logMessage(
                            f"Layer '{layer.name()}': {conversion_errors} errori di conversione rilevati. Valori impostati a NULL.", 
                            "QUtility", 
                            Qgis.Warning
                        )
                
                else:
                    # Copia i valori dal campo esistente
                    for feature in layer.getFeatures():
                        try:
                            value = feature[field_idx]
                            
                            if value is None or (isinstance(value, QVariant) and value.isNull()):
                                layer.changeAttributeValue(feature.id(), new_idx, None)
                            else:
                                if qvariant_type == QVariant.Int:
                                    try:
                                        value = int(value) if value not in (None, "") else None
                                    except (ValueError, TypeError):
                                        value = 0
                                elif qvariant_type == QVariant.Double:
                                    try:
                                        value = float(value) if value not in (None, "") else None
                                    except (ValueError, TypeError):
                                        value = 0.0
                                
                                layer.changeAttributeValue(feature.id(), new_idx, value)
                                
                        except Exception as e:
                            print(f"Errore nella conversione di un valore: {str(e)}")
                            continue
                
                # Elimina il vecchio campo
                layer.deleteAttribute(field_idx)
                layer.updateFields()
                
                # Prova a rinominare il campo
                try:
                    # Ottieni l'indice aggiornato dopo l'eliminazione del campo originale
                    updated_idx = layer.fields().indexFromName(temp_field_name)
                    layer.renameAttribute(updated_idx, new_field_name)
                except Exception:
                    # Se il rinomino fallisce, lasciamo il campo con il nome temporaneo
                    error_layers.append(f"{layer.name()} (rinomino fallito)")
                    layer.commitChanges()
                    continue
                
                # Salva le modifiche
                layer.commitChanges()
                
                # Aggiungi ai layer di successo
                success_layers.append(layer.name())
                
            except Exception as e:
                error_layers.append(f"{layer.name()} ({str(e)})")
                if layer.isEditable():
                    layer.rollBack()
        
        # Chiudi il popup di elaborazione
        progress_dialog.accept()
        
        # Riabilita i controlli
        self.fmrun.setEnabled(True)
        self.fmlayerlist.setEnabled(True)
        self.fmfield.setEnabled(True)
        self.fmtype.setEnabled(True)
        self.fmlun.setEnabled(True)
        self.toggle_precision_field()  # Riattiva/disattiva il campo precisione in base al tipo
        
        # Crea un messaggio di completamento dettagliato
        result_message = ""
        
        if success_layers:
            result_message += f"Modifica completata con successo per {len(success_layers)} layer:\n"
            result_message += "\n".join(f"- {layer}" for layer in success_layers)
        
        if error_layers:
            if result_message:
                result_message += "\n\n"
            result_message += f"Errori in {len(error_layers)} layer:\n"
            result_message += "\n".join(f"- {layer}" for layer in error_layers)
        
        if not result_message:
            result_message = "Nessun layer elaborato."
        
        # Mostra messaggio di completamento
        QMessageBox.information(self, "Completed", result_message)

        # Aggiorna la lista dei campi
        self.update_fields()
    
    def setup_overlaps_counter(self):
        """Inizializza i componenti per Overlaps Counter"""
        # Imposta il filtro per mostrare SOLO layer puntuali e lineari (esclude poligoni)
        self.lyr1_oc.setFilters(QgsMapLayerProxyModel.LineLayer)
        self.lyr2_oc.setFilters(QgsMapLayerProxyModel.LineLayer)

        # Configura i checkbox per "Solo elementi selezionati"
        self.ckbox_lyr1_oc.setChecked(False)  # Predefinito: usa tutti gli elementi
        self.ckbox_lyr2_oc.setChecked(False)  # Predefinito: usa tutti gli elementi
        
        # Configura il widget per il file di output
        self.cartout_oc.setStorageMode(QgsFileWidget.SaveFile)
        self.cartout_oc.setDialogTitle("Seleziona file di output")
        
        # Popola il combo box dei formati di output
        self.formatout_oc.clear()
        self.formatout_oc.addItem("Shapefile (.shp)", "shp")
        self.formatout_oc.addItem("GeoPackage (.gpkg)", "gpkg")
        self.formatout_oc.addItem("GeoJSON (.geojson)", "geojson")
        self.formatout_oc.addItem("CSV (.csv)", "csv")
        
        # Imposta il formato predefinito
        self.formatout_oc.setCurrentText("Shapefile (.shp)")
        
        # Connetti il pulsante al metodo per eseguire il contatore di sovrapposizioni
        self.btn_oc.clicked.connect(self.run_overlaps_counter)
        
        # Connetti i segnali per aggiornare l'estensione del file di output
        self.formatout_oc.currentTextChanged.connect(self.update_output_extension)

        # Connetti i segnali dei layer per abilitare/disabilitare i checkbox
        self.lyr1_oc.layerChanged.connect(self.update_checkbox_states)
        self.lyr2_oc.layerChanged.connect(self.update_checkbox_states)
        
        # Imposta i filtri per il file di output basati sul formato selezionato
        self.update_output_extension()

        # Aggiorna lo stato iniziale dei checkbox
        self.update_checkbox_states()

    def update_output_extension(self):
        """Aggiorna l'estensione del file di output in base al formato selezionato"""
        
        format_text = self.formatout_oc.currentText()
        
        if "Shapefile" in format_text:
            self.cartout_oc.setFilter("Shapefile (*.shp)")
        elif "GeoPackage" in format_text:
            self.cartout_oc.setFilter("GeoPackage (*.gpkg)")
        elif "GeoJSON" in format_text:
            self.cartout_oc.setFilter("GeoJSON (*.geojson)")
        elif "CSV" in format_text:
            self.cartout_oc.setFilter("CSV (*.csv)")

    def update_checkbox_states(self):
        """Aggiorna lo stato dei checkbox in base ai layer selezionati"""
        
        # Controlla se i layer hanno delle selezioni attive
        layer1 = self.lyr1_oc.currentLayer()
        layer2 = self.lyr2_oc.currentLayer()
        
        # CONFIGURAZIONE DINAMICA DEI CHECKBOX:
        if layer1 and layer1.selectedFeatureCount() > 0:
            self.ckbox_lyr1_oc.setEnabled(True)
            self.ckbox_lyr1_oc.setText(f"Selected records only ({layer1.selectedFeatureCount()})")
        else:
            self.ckbox_lyr1_oc.setEnabled(False)
            self.ckbox_lyr1_oc.setChecked(False)
            self.ckbox_lyr1_oc.setText("Selected records only")

        if layer2 and layer2.selectedFeatureCount() > 0:
            self.ckbox_lyr2_oc.setEnabled(True)
            self.ckbox_lyr2_oc.setText(f"Selected records only ({layer2.selectedFeatureCount()})")
        else:
            self.ckbox_lyr2_oc.setEnabled(False)
            self.ckbox_lyr2_oc.setChecked(False)
            self.ckbox_lyr2_oc.setText("Selected records only")

    def run_overlaps_counter(self):
        """Esegue l'elaborazione Overlaps Counter"""
        try:
            # Importa il metodo dalla classe principale
            # Assumendo che self.parent sia l'istanza di QUtility
            parent_plugin = None
            
            # Cerca l'istanza del plugin QUtility
            from qgis.utils import plugins
            if 'qutility' in plugins:
                parent_plugin = plugins['qutility']
            
            if parent_plugin and hasattr(parent_plugin, 'run_overlaps_counter'):
                parent_plugin.run_overlaps_counter()
            else:
                QMessageBox.warning(self, "Error", "Unable to find processing method")

        except Exception as e:
            QMessageBox.critical(self, "Error", f"Error starting processing: {str(e)}")

    #Metodi di gestione Batch Merger
    def setup_batch_merger(self):
        """Inizializza i componenti per Batch Merger"""
        from qgis.core import QgsProject
        
        # Popola la lista dei layer
        self.populate_batch_merger_layers()
        
        # Imposta CRS predefinito
        project_crs = QgsProject.instance().crs()
        if project_crs.isValid():
            self.crs_bm.setCrs(project_crs)
        
        # Imposta checkbox default per NON aggiungere i campi
        self.cb_bm.setChecked(False)
        
        # CONNETTI IL PULSANTE - CRUCIALE!
        self.run_btn_bm.clicked.connect(self.run_batch_merger)

        # Aggiorna lista quando cambiano i layer del progetto
        QgsProject.instance().layersAdded.connect(self.populate_batch_merger_layers)
        QgsProject.instance().layersRemoved.connect(self.populate_batch_merger_layers)
        
        # Aggiorna lista quando cambiano i layer del progetto
        QgsProject.instance().layersAdded.connect(self.populate_batch_merger_layers)
        QgsProject.instance().layersRemoved.connect(self.populate_batch_merger_layers)

    def populate_batch_merger_layers(self):
        """Popola la lista dei layer vettoriali con informazioni dettagliate"""
        from qgis.core import QgsProject, QgsVectorLayer, QgsMapLayer
        from PyQt5 import QtWidgets
        from PyQt5.QtCore import Qt
        from PyQt5.QtGui import QIcon
        import os
        
        self.lyrlist_bm.clear()
        
        # Ottieni tutti i layer dal progetto
        layers = QgsProject.instance().mapLayers().values()
        vector_layers = []
        
        # ✨ MIGLIORAMENTO: Controllo più inclusivo per layer vettoriali
        for layer in layers:
            # Controllo multiplo per essere sicuri di catturare tutti i layer vettoriali
            is_vector = False
            
            # Metodo 1: isinstance check (il più affidabile)
            if isinstance(layer, QgsVectorLayer):
                is_vector = True
            
            # Metodo 2: controllo del tipo layer
            elif hasattr(layer, 'type') and layer.type() == QgsMapLayer.VectorLayer:
                is_vector = True
                
            # Metodo 3: controllo della presenza di geometrie
            elif hasattr(layer, 'geometryType') and hasattr(layer, 'featureCount'):
                is_vector = True
            
            if is_vector and layer.isValid():
                vector_layers.append(layer)
        
        # Ordina i layer per nome
        vector_layers.sort(key=lambda x: x.name().lower())
        
        print(f"🔍 Found {len(vector_layers)} vector layers in project")
        
        # ✨ MIGLIORAMENTO: Crea elementi con informazioni dettagliate
        for layer in vector_layers:
            try:
                # Nome base del layer e pulisci nome da caratteri speciali
                layer_name = layer.name()
                
                # ✨ NUOVO: Rileva il formato del layer
                layer_source = layer.source()
                format_info = self.detect_layer_format(layer_source)
                
                # ✨ NUOVO: Informazioni sulla geometria
                geometry_info = "Unknown"
                try:
                    geom_type = layer.geometryType()
                    geometry_types = {
                        0: "Point",
                        1: "Line", 
                        2: "Polygon",
                        3: "Unknown",
                        4: "No geometry"
                    }
                    geometry_info = geometry_types.get(geom_type, "Unknown")
                except:
                    pass
                
                # ✨ NUOVO: Conta features in modo sicuro
                feature_count = "?"
                try:
                    if hasattr(layer, 'featureCount'):
                        feature_count = str(layer.featureCount())
                except:
                    feature_count = "N/A"
                
                # ✨ NUOVO: Nome visualizzato con formato
                if format_info != 'unknown':
                    display_name = f"{layer_name} [{format_info.upper()}]"
                else:
                    display_name = layer_name
                
                # Crea l'elemento della lista
                item = QtWidgets.QListWidgetItem(display_name)
                item.setData(Qt.UserRole, layer)
                
                # ✨ MIGLIORAMENTO: Tooltip dettagliato e colorato
                crs_info = "Unknown CRS"
                try:
                    if hasattr(layer, 'crs') and layer.crs().isValid():
                        crs_info = layer.crs().authid()
                except:
                    pass
                
                # Tooltip con tutte le informazioni
                tooltip_lines = [
                    f"📋 Name: {layer_name}",
                    f"📁 Format: {format_info.upper()}",
                    f"🎯 Geometry: {geometry_info}", 
                    f"📊 Features: {feature_count}",
                    f"🗺️ CRS: {crs_info}",
                    f"📂 Source: {os.path.basename(layer_source) if layer_source else 'Unknown'}"
                ]
                
                item.setToolTip("\n".join(tooltip_lines))
                
                # Aggiungi alla lista
                self.lyrlist_bm.addItem(item)
                
                # Debug info
                print(f"  ✓ Added: {display_name} ({geometry_info}, {feature_count} features)")
                
            except Exception as e:
                # Se c'è un errore con un layer specifico, continua con gli altri
                print(f"  ❌ Error processing layer {layer.name()}: {str(e)}")
                # Aggiungi comunque il layer ma con informazioni minimali
                item = QtWidgets.QListWidgetItem(layer.name())
                item.setData(Qt.UserRole, layer)
                item.setToolTip(f"Layer: {layer.name()}\nError loading details: {str(e)}")
                self.lyrlist_bm.addItem(item)
        
        print(f"✅ Populated Batch Merger with {self.lyrlist_bm.count()} layers")


    def detect_layer_format(self, layer_source):
        """
        Rileva il formato di un layer dal suo percorso sorgente
        Versione migliorata che gestisce più casi
        """
        if not layer_source:
            return 'unknown'
        
        source_lower = str(layer_source).lower()
        
        # Controlli più specifici
        if '.shp' in source_lower or 'shapefile' in source_lower:
            return 'shp'
        elif '.gpkg' in source_lower or 'geopackage' in source_lower:
            return 'gpkg'
        elif '.geojson' in source_lower or (('.json' in source_lower) and 'geo' in source_lower):
            return 'geojson'
        elif '.gml' in source_lower:
            return 'gml'
        elif '.kml' in source_lower:
            return 'kml'
        elif '.sqlite' in source_lower or '.db' in source_lower:
            return 'sqlite'
        elif '.csv' in source_lower:
            return 'csv'
        elif 'postgis' in source_lower or 'postgresql' in source_lower:
            return 'postgres'
        elif 'memory' in source_lower:
            return 'memory'
        elif 'ogr' in source_lower or 'http' in source_lower:
            return 'web'
        else:
            return 'unknown'


    # BONUS: Funzione per aggiornare la lista quando cambiano i layer
    def refresh_batch_merger_layers(self):
        """Aggiorna la lista mantenendo le selezioni correnti"""
        # Salva le selezioni correnti
        selected_layer_names = []
        for item in self.lyrlist_bm.selectedItems():
            layer = item.data(Qt.UserRole)
            if layer:
                selected_layer_names.append(layer.name())
        
        # Ricarica la lista
        self.populate_batch_merger_layers()
        
        # Ripristina le selezioni
        for i in range(self.lyrlist_bm.count()):
            item = self.lyrlist_bm.item(i)
            layer = item.data(Qt.UserRole)
            if layer and layer.name() in selected_layer_names:
                item.setSelected(True)
        
        print(f"🔄 Refreshed layer list, restored {len(selected_layer_names)} selections")
                
    def run_batch_merger(self):
        """Bridge verso il metodo principale"""
        try:
            from qgis.utils import plugins
            
            if 'qutility' in plugins:
                plugins['qutility'].run_batch_merger()
            else:
                from PyQt5.QtWidgets import QMessageBox
                QMessageBox.warning(self, "Error", "QUtility plugin not found")
        except Exception as e:
            from PyQt5.QtWidgets import QMessageBox
            QMessageBox.critical(self, "Error", f"Error: {str(e)}")

    # METODI GETTER - Recuperano i valori dall'interfaccia
    def get_selected_batch_merger_layers(self):
        """Restituisce i layer selezionati"""
        from PyQt5.QtCore import Qt
        
        selected_layers = []
        for item in self.lyrlist_bm.selectedItems():
            layer = item.data(Qt.UserRole)
            if layer:
                selected_layers.append(layer)
        return selected_layers

    def get_batch_merger_target_crs(self):
        """Restituisce il CRS target"""
        return self.crs_bm.crs()

    def get_batch_merger_add_fields(self):
        """Restituisce True se deve aggiungere i campi layer/path"""
        return self.cb_bm.isChecked()
    
    def handle_info_link_click(self, url):
        """Gestisce i click sui link nel tab Info"""
        link = url.toString()
        
        tab_map = {
            "#field_modifier": 1,
            "#layer_renamer": 2,
            "#smart_layer_loader": 3,
            "#exclude_features": 4,
            "#batch_converter": 5,
            "#overlaps_counter": 6,
            "#batch_merger": 7,
        }
        
        if link in tab_map:
            self.tabWidget.setCurrentIndex(tab_map[link])