
from collections import defaultdict

import numbers

from typing import Union, Dict, Tuple

from qgis.core import QgsProject, QgsVectorLayer
from qgis.core import QgsMessageLog
from qgis.core import Qgis

import matplotlib.pyplot as plt


def get_symbology_field(layer) -> Union[None, str]:
    """
    Restituisce il nome del campo su cui è basata la simbologia del layer,
    se è un singolo campo; altrimenti None.
    """

    try:

        # Verify layer is valid
        if not isinstance(layer, QgsVectorLayer):
            QgsMessageLog.logMessage(
                f"Invalid layer type: expected QgsVectorLayer, got {type(layer)}",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )
            return None

        if not layer.isValid():
            QgsMessageLog.logMessage(
                f"Layer '{layer.name()}' is not valid",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )
            return None

        renderer = layer.renderer()
        if renderer is None:
            return None

        rtype = renderer.type()

        if rtype in ("categorizedSymbol", "graduatedSymbol"):

            attr = renderer.classAttribute()

            # Controlla se 'attr' è il nome di un campo reale
            if attr in [f.name() for f in layer.fields()]:
                return attr
            else:
                return None
        else:
            return None

    except Exception as e:

        QgsMessageLog.logMessage(
            f"Error getting symbology field: {e!r}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return None


def extract_qgis_colors(layer) -> Union[None, defaultdict[str, Tuple[numbers.Real, numbers.Real, numbers.Real]]]:
    """
    Estrae le coppie (etichetta, colore) dalla simbologia di un layer QGIS,
    mantenendo l'ordine della legenda.

    :param layer: QgsVectorLayer
    :return: lista di tuple (etichetta, (R, G, B)), con RGB normalizzato [0-1]
    """

    try:

        # Verify layer is valid
        if not isinstance(layer, QgsVectorLayer):
            QgsMessageLog.logMessage(
                f"Invalid layer type: expected QgsVectorLayer, got {type(layer)}",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )
            return None

        if not layer.isValid():
            QgsMessageLog.logMessage(
                f"Layer '{layer.name()}' is not valid",
                "qgSurf",
                Qgis.MessageLevel.Warning
            )
            return None

        renderer = layer.renderer()
        if renderer is None:
            return None

        results = defaultdict(tuple)

        if renderer.type() == "categorizedSymbol":

            for cat in renderer.categories():
                qcolor = cat.symbol().color()
                results[cat.label()] = (qcolor.redF(), qcolor.greenF(), qcolor.blueF())

        elif renderer.type() == "graduatedSymbol":

            for rng in renderer.ranges():
                qcolor = rng.symbol().color()
                results[rng.label()] = (qcolor.redF(), qcolor.greenF(), qcolor.blueF())

        elif renderer.type() == "singleSymbol":

            qcolor = renderer.symbol().color()
            results["all features"] = (qcolor.redF(), qcolor.greenF(), qcolor.blueF())

        else:

            raise ValueError(f"Renderer type not managed: {renderer.type()}")

        return results

    except Exception as e:

        QgsMessageLog.logMessage(
            f"Error extracting QGIS colors: {e!r}",
            "qgSurf",
            Qgis.MessageLevel.Warning
        )
        return None


if __name__ == "__main__":

    # --- Esempio d'uso ---
    layer_name = "NomeDelTuoLayer"  # Sostituisci con il nome effettivo
    layer = QgsProject.instance().mapLayersByName(layer_name)[0]

    color_map = extract_qgis_colors(layer)

    # --- Esempio con Matplotlib ---
    labels = [lbl for lbl, _ in color_map]
    colors = [col for _, col in color_map]

    plt.bar(range(len(labels)), [1] * len(labels), color=colors)
    plt.xticks(range(len(labels)), labels, rotation=45, ha='right')
    plt.tight_layout()
    plt.show()
