from enum import Enum

from qgis.core import (
    Qgis,
    QgsMapLayer,
    QgsProject,
    QgsProviderConnectionException,
    QgsProviderRegistry,
    QgsVectorLayer,
)
from qgis.PyQt.QtXml import QDomDocument
from qgis.utils import iface

from plui_versionning.__about__ import DIR_PLUGIN_ROOT
from plui_versionning.toolbelt import PlgLogger, PlgOptionsManager


class Mode(Enum):
    ADD = 1  # Ajout de la couche au projet
    REPLACE = 2  # Suppression des couches du même sous-groupe (thématique) avant d'ajouter les couches d'une version


class PLUiLoader:
    def __init__(
        self,
        mode,
        procedure,
        classe,
        thematique,
        projet,
        version,
        version_id,
        action_save_style,
    ) -> None:
        self.plg_settings = PlgOptionsManager()
        self.log = PlgLogger().log

        # DB connection
        self.db_connection_uri = self.plg_settings.get_plg_settings().db_connection_uri
        self.db_schema_name = self.plg_settings.get_plg_settings().db_schema_name

        self.qgs_project = QgsProject.instance()
        self.root = QgsProject.instance().layerTreeRoot()
        self.mode = mode
        self.procedure = procedure
        self.classe = classe
        self.thematique = thematique
        self.projet = projet
        self.version = version
        self.version_id = version_id
        self.action_save_plui_style = action_save_style

    def checkVersionStatus(self):
        sql = """
            select editable
            from \"{0}\".v_pluiv_versions pv
            join \"{0}\".pluiv_versionlibellestatut s on pv.statut = s.valeur
            where pv.id_version = '{1}'
        """.format(
            self.db_schema_name, self.version
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(sql)
            return result[0][0]
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        return False

    def addLayerGroup(self, parentGroup, label):
        layerGroup = parentGroup.findGroup(label)
        if layerGroup is None:
            layerGroup = parentGroup.addGroup(label)
        return layerGroup

    def insertLayerGroup(self, parentGroup, label, position):
        layerGroup = parentGroup.findGroup(label)
        if layerGroup is None:
            layerGroup = parentGroup.insertGroup(position, label)
        return layerGroup

    def loadLayer(
        self, parentGroup, nameGroup, primaryKey, tableName, geomName, layerName
    ):
        match parentGroup:
            case "Zonage":
                position = 0
            case "Prescription":
                position = 1
            case "Information":
                position = 2
            case "Habillage":
                position = 3
            case _:
                position = 0
        group = self.insertLayerGroup(self.versionGroup, parentGroup, position)

        vGroup = self.addLayerGroup(group, nameGroup)

        source = """{} key='{}' table="{}"."{}" ({})
        sql=/*&*/idurba='{}'/*&*/""".format(
            self.db_connection_uri,
            primaryKey,
            self.db_schema_name,
            tableName,
            geomName,
            self.version,
        )
        vectorLayer = QgsVectorLayer(source, layerName, "postgres")
        if not self.checkVersionStatus():
            vectorLayer.setReadOnly(True)
        else:
            vectorLayer.setReadOnly(False)
        if len(self.qgs_project.mapLayersByName(vectorLayer.name())) == 0:
            layer = self.qgs_project.addMapLayer(vectorLayer, False)

            if "SUPPR" in nameGroup:
                geom_type = vectorLayer.geometryType()
                match geom_type:
                    case Qgis.GeometryType.Point:
                        style_file = str(
                            DIR_PLUGIN_ROOT / "resources/style_pct_SUPPR.qml"
                        )
                    case Qgis.GeometryType.Line:
                        style_file = str(
                            DIR_PLUGIN_ROOT / "resources/style_lin_SUPPR.qml"
                        )
                    case Qgis.GeometryType.Polygon:
                        style_file = str(
                            DIR_PLUGIN_ROOT / "resources/style_surf_SUPPR.qml"
                        )
                    case _:
                        style_file = str(
                            DIR_PLUGIN_ROOT / "resources/style_pct_SUPPR.qml"
                        )
                layer.loadNamedStyle(style_file)
            else:
                styleqml = self.loadStyle(geomName)
                if styleqml:
                    styledoc = QDomDocument()
                    styledoc.setContent(styleqml)
                    layer.importNamedStyle(styledoc, QgsMapLayer.AllStyleCategories)
            vGroup.addLayer(layer)
            # Context menu
            iface.addCustomActionForLayerType(
                self.action_save_plui_style, "", QgsMapLayer.VectorLayer, False
            )
            iface.addCustomActionForLayer(self.action_save_plui_style, layer)

    def loadLayers(self):
        print(f"Procedure : {self.procedure}")
        print(f"Classe : {self.classe}")
        print(f"Projet : {self.projet}")
        print(f"Version : {self.version}")
        pluGroup = self.insertLayerGroup(
            self.root, f"PLUi - Procédure {str(self.procedure)}", 0
        )

        # Administration
        adminGroup = self.insertLayerGroup(pluGroup, "Administration", 0)
        valeursCNIGGroup = self.addLayerGroup(adminGroup, "Liste de valeurs CNIG")
        valeursCTGroup = self.addLayerGroup(adminGroup, "Liste de valeurs CT")
        stylesGroup = self.addLayerGroup(adminGroup, "Liste des styles")
        CNIG_layers = [
            "destinationtype",
            "documenturbatype",
            "etatdocumenttype",
            "formdomitype",
            "informationurbatype",
            "prescriptionurbatype",
            "zoneurbatype",
        ]
        CT_layers = [
            "pluiv_informationurbatype_ct",
            "pluiv_prescriptionurbatype_ct",
            "pluiv_zoneurbatype_ct",
        ]
        for layer in CNIG_layers:
            source = """{} key='id' table="{}"."{}"
            """.format(
                self.db_connection_uri, self.db_schema_name, layer
            )
            CNIG_layer = QgsVectorLayer(source, layer, "postgres")
            if len(self.qgs_project.mapLayersByName(CNIG_layer.name())) == 0:
                layer = self.qgs_project.addMapLayer(CNIG_layer, False)
                valeursCNIGGroup.addLayer(layer)

        for layer in CT_layers:
            source = """{} key='id' table="{}"."{}"
            """.format(
                self.db_connection_uri, self.db_schema_name, layer
            )
            CT_layer = QgsVectorLayer(source, layer, "postgres")
            if len(self.qgs_project.mapLayersByName(CT_layer.name())) == 0:
                layer = self.qgs_project.addMapLayer(CT_layer, False)
                valeursCTGroup.addLayer(layer)

        source = f'{self.db_connection_uri} key=\'id\' table="public"."layer_styles"'
        styles_layer = QgsVectorLayer(source, "layer_styles", "postgres")
        if len(self.qgs_project.mapLayersByName(styles_layer.name())) == 0:
            layer = self.qgs_project.addMapLayer(styles_layer, False)
            stylesGroup.addLayer(layer)

        # Versionning
        self.versionGroup = self.insertLayerGroup(pluGroup, "Versionning", 1)
        layers = [
            {
                "parentGroupName": "Zonage",
                "groupName": f"Zonage ({self.version})",
                "primaryKey": "idzoneurba",
                "tableName": "v_zoneurba",
                "geomName": "geom",
                "layerName": f"Zonage : {self.version}",
            },
            {
                "parentGroupName": "Zonage",
                "groupName": f"SUPPR ({self.version})",
                "primaryKey": "idzoneurba",
                "tableName": "v_zoneurba_suppr",
                "geomName": "geom",
                "layerName": f"ZonageSuppr : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_pct",
                "geomName": "geom_pct",
                "layerName": f"Prescription_pct : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_pct_suppr",
                "geomName": "geom_pct",
                "layerName": f"Prescription_pct_suppr : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_lin",
                "geomName": "geom_lin",
                "layerName": f"Prescription_lin : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_lin_suppr",
                "geomName": "geom_lin",
                "layerName": f"Prescription_lin_suppr : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_surf",
                "geomName": "geom_surf",
                "layerName": f"Prescription_surf : {self.version}",
            },
            {
                "parentGroupName": "Prescription",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_surf_suppr",
                "geomName": "geom_surf",
                "layerName": f"Prescription_surf_suppr : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_pct",
                "geomName": "geom_pct",
                "layerName": f"Information_pct : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_pct_suppr",
                "geomName": "geom_pct",
                "layerName": f"Information_pct_suppr : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_lin",
                "geomName": "geom_lin",
                "layerName": f"Information_lin : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_lin_suppr",
                "geomName": "geom_lin",
                "layerName": f"Information_lin_suppr : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_surf",
                "geomName": "geom_surf",
                "layerName": f"Information_surf : {self.version}",
            },
            {
                "parentGroupName": "Information",
                "groupName": f"{self.thematique} SUPPR ({self.version})",
                "primaryKey": "idinformation",
                "tableName": "v_information_surf_suppr",
                "geomName": "geom_surf",
                "layerName": f"Information_surf_suppr : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_txt",
                "geomName": "geom_txt",
                "layerName": f"Habillage_txt : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} SUPPR ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_txt_suppr",
                "geomName": "geom_txt",
                "layerName": f"Habillage_txt_suppr : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_pct",
                "geomName": "geom_pct",
                "layerName": f"Habillage_pct : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} SUPPR ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_pct_suppr",
                "geomName": "geom_pct",
                "layerName": f"Habillage_pct_suppr : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_lin",
                "geomName": "geom_lin",
                "layerName": f"Habillage_lin : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} SUPPR ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_lin_suppr",
                "geomName": "geom_lin",
                "layerName": f"Habillage_lin_suppr : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_surf",
                "geomName": "geom_surf",
                "layerName": f"Habillage_surf : {self.version}",
            },
            {
                "parentGroupName": "Habillage",
                "groupName": f"Habillage {self.thematique} SUPPR ({self.version})",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_surf_suppr",
                "geomName": "geom_surf",
                "layerName": f"Habillage_surf_suppr : {self.version}",
            },
        ]
        # Mode REPLACE --> supprimer toutes les couches du groupe
        if self.mode == Mode.REPLACE:
            currentClasseGroup = self.versionGroup.findGroup(self.classe)
            if currentClasseGroup is not None:
                print("Suppression des couches de " + self.classe)
                currentClasseGroup.removeAllChildren()
        for layer in layers:
            if layer["parentGroupName"] == self.classe:
                self.loadLayer(
                    layer["parentGroupName"],
                    layer["groupName"],
                    layer["primaryKey"],
                    layer["tableName"],
                    layer["geomName"],
                    layer["layerName"],
                )

    def loadStyle(self, geomName):
        styleqml = None

        query = """
            select "{}".qgis_charger_style({}, '{}')
        """.format(
            self.db_schema_name, self.version_id, geomName
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)
            if result:
                id_style = result[0][0]
            else:
                return None
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)

        query = """
            select styleqml from layer_styles
            where id = {}
        """.format(
            id_style
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)
            if result:
                styleqml = result[0][0]
            else:
                return None
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)

        return styleqml
