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.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, 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.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 loadLayer(self, nameGroup, primaryKey, tableName, geomName, layerName):
        styleqml = self.loadStyle(geomName)
        group = self.versionGroup.findGroup(nameGroup)
        if group is None:
            group = self.versionGroup.addGroup(nameGroup)

        cartoGroup = group.findGroup("Espace carto")
        if cartoGroup is None:
            cartoGroup = group.insertGroup(1, "Espace carto")

        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 styleqml:
                styledoc = QDomDocument()
                styledoc.setContent(styleqml)
                layer.importNamedStyle(styledoc, QgsMapLayer.Symbology)
            cartoGroup.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.root.findGroup("PLUi - Procédure " + str(self.procedure))
        if pluGroup is None:
            pluGroup = self.root.insertGroup(
                0, "PLUi - Procédure " + str(self.procedure)
            )

        # Cycles de numérisation
        source = """{} key='id' table="{}"."v_pluiv_procedures" """.format(
            self.db_connection_uri, self.db_schema_name
        )
        procedureLayer = QgsVectorLayer(source, "Cycles de numérisation", "postgres")
        if len(self.qgs_project.mapLayersByName(procedureLayer.name())) == 0:
            layer = self.qgs_project.addMapLayer(procedureLayer, False)
            pluGroup.addLayer(layer)

        # Référentiels
        referentielGroup = pluGroup.findGroup("Référentiels")
        if referentielGroup is None:
            referentielGroup = pluGroup.insertGroup(2, "Référentiels")
        source = """{} key='id' table="{}"."ref_limites_communales" (geom)""".format(
            self.db_connection_uri, self.db_schema_name
        )
        communesLayer = QgsVectorLayer(source, "Limites communales", "postgres")
        if len(self.qgs_project.mapLayersByName(communesLayer.name())) == 0:
            layer = self.qgs_project.addMapLayer(communesLayer, False)
            referentielGroup.addLayer(layer)

        # Projets
        projetGroup = pluGroup.findGroup("Projets")
        if projetGroup is None:
            projetGroup = pluGroup.insertGroup(1, "Projets")
        source = """{} key='id' table="{}"."v_pluiv_projets"
        sql="procedure_plui"='{}'""".format(
            self.db_connection_uri, self.db_schema_name, self.procedure
        )
        projetLayer = QgsVectorLayer(source, "projet", "postgres")
        if len(self.qgs_project.mapLayersByName(projetLayer.name())) == 0:
            layer = self.qgs_project.addMapLayer(projetLayer, False)
            projetGroup.addLayer(layer)

        # Versionning
        self.versionGroup = pluGroup.findGroup("Versionning")
        if self.versionGroup is None:
            self.versionGroup = pluGroup.insertGroup(0, "Versionning")
        layers = [
            {
                "groupName": "Zonage",
                "primaryKey": "idzoneurba",
                "tableName": "v_zoneurba",
                "geomName": "geom",
                "layerName": "Zonage :" + self.version,
            },
            {
                "groupName": "Zonage",
                "primaryKey": "idzoneurba",
                "tableName": "v_zoneurba_suppr",
                "geomName": "geom",
                "layerName": "ZonageSuppr :" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_pct",
                "geomName": "geom_pct",
                "layerName": "Prescription_pct :" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_pct_suppr",
                "geomName": "geom_pct",
                "layerName": "Prescription_pct_suppr :" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_lin",
                "geomName": "geom_lin",
                "layerName": "Prescription_lin :" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_lin_suppr",
                "geomName": "geom_lin",
                "layerName": "Prescription_lin_suppr :" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_surf",
                "geomName": "geom_surf",
                "layerName": "Prescription_surf:" + self.version,
            },
            {
                "groupName": "Prescription",
                "primaryKey": "idprescription",
                "tableName": "v_prescription_surf_suppr",
                "geomName": "geom_surf",
                "layerName": "Prescription_surf_suppr :" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_pct",
                "geomName": "geom_pct",
                "layerName": "Information_pct :" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_pct_suppr",
                "geomName": "geom_pct",
                "layerName": "Information_pct_suppr :" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_lin",
                "geomName": "geom_lin",
                "layerName": "Information_lin :" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_lin_suppr",
                "geomName": "geom_lin",
                "layerName": "Information_lin_suppr :" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_surf",
                "geomName": "geom_surf",
                "layerName": "Information_surf:" + self.version,
            },
            {
                "groupName": "Information",
                "primaryKey": "idinformation",
                "tableName": "v_information_surf_suppr",
                "geomName": "geom_surf",
                "layerName": "Information_surf_suppr :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_txt",
                "geomName": "geom_txt",
                "layerName": "Habillage_txt :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_txt_suppr",
                "geomName": "geom_txt",
                "layerName": "Habillage_txt_suppr :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_pct",
                "geomName": "geom_pct",
                "layerName": "Habillage_pct :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_pct_suppr",
                "geomName": "geom_pct",
                "layerName": "Habillage_pct_suppr :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_lin",
                "geomName": "geom_lin",
                "layerName": "Habillage_lin :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_lin_suppr",
                "geomName": "geom_lin",
                "layerName": "Habillage_lin_suppr :" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_surf",
                "geomName": "geom_surf",
                "layerName": "Habillage_surf:" + self.version,
            },
            {
                "groupName": "Habillage",
                "primaryKey": "idhabillage",
                "tableName": "v_habillage_surf_suppr",
                "geomName": "geom_surf",
                "layerName": "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:
                cartoGroup = currentClasseGroup.findGroup("Espace carto")
                if cartoGroup is not None:
                    print("Suppression des couches de " + self.classe)
                    cartoGroup.removeAllChildren()
        for layer in layers:
            if layer["groupName"] == self.classe:
                self.loadLayer(
                    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
