from pathlib import Path

from qgis.core import (
    NULL,
    Qgis,
    QgsApplication,
    QgsProviderConnectionException,
    QgsProviderRegistry,
)
from qgis.PyQt import uic
from qgis.PyQt.QtCore import QDateTime, pyqtSignal
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QMessageBox, QWidget

from plui_versionning.core.utils import catch_postgres_exception
from plui_versionning.toolbelt import PlgLogger, PlgOptionsManager

REFERENCE = "Référence"
EN_COURS = "En cours"
FIGEE = "Figée"
ABANDONNEE = "Abandonnée"
VALIDEE = "Validée"

STATUS = [REFERENCE, EN_COURS, FIGEE, ABANDONNEE, VALIDEE]


class VersionDetailsWidget(QWidget):
    # Signal pour charger une version dans le projet QGIS
    loadVersion = pyqtSignal(object)

    # Signal pour rafraichir les versions dans le TreeWidget
    refreshVersions = pyqtSignal(object)

    def __init__(self, parent: QWidget = None):
        """
        QWidget Afficher/Modifier les détails d'une version

        Args:
            parent: parent QWidget
        """
        super().__init__(parent)
        self.parent = parent
        self.log = PlgLogger().log
        self.plg_settings = PlgOptionsManager()

        # 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

        uic.loadUi(Path(__file__).parent / f"{Path(__file__).stem}.ui", self)

        self.id_version = None
        self.read_mode = True
        self.edit_icon = QIcon(QgsApplication.iconPath("mActionToggleEditing.svg"))
        self.save_icon = QIcon(QgsApplication.iconPath("mActionSaveAllEdits.svg"))
        self.edit_tooltip = "Editer les détails d'une version"
        self.save_tooltip = "Sauvegarder les changements"
        self.btn_edit.setIcon(self.edit_icon)
        self.btn_edit.setToolTip(self.edit_tooltip)
        self.btn_edit.clicked.connect(self.editer_details)
        self.btn_charger_version.clicked.connect(self.charger_version)
        self.btn_dupliquer_version.setEnabled(False)
        self.btn_dupliquer_version.clicked.connect(self.dupliquer_version)
        self.set_read_only_widgets(self.read_mode)

    def set_read_only_widgets(self, read_only: bool):
        """Set read only mode and enable/disable widgets

        Args:
            read_only (boolean): Parameter to set/unset read only mode and enable/disable widget
        """
        self.edit_intitule.setReadOnly(read_only)
        self.edit_commentaires.setReadOnly(read_only)
        self.edit_intitule.setEnabled(not read_only)
        self.edit_commentaires.setEnabled(not read_only)
        self.lbl_nouveau_statut.setVisible(not read_only)
        self.cb_nouveau_statut.setVisible(not read_only)
        self.btn_edit.setIcon(self.edit_icon if read_only else self.save_icon)
        self.btn_edit.setToolTip(self.edit_tooltip if read_only else self.save_tooltip)
        self.btn_edit.setDown(not read_only)

    def fill_cb_nouveau_statut(self):
        """Remplit la liste déroulante des nouveaux statuts possibles en fonction du statut actuel de la version"""
        # Clear combobox
        self.cb_nouveau_statut.clear()

        statut = self.lbl_statut_value.text()
        evol_statut = {
            REFERENCE: [REFERENCE],
            EN_COURS: [EN_COURS, FIGEE, ABANDONNEE],
            FIGEE: [FIGEE, EN_COURS, ABANDONNEE, VALIDEE],
            ABANDONNEE: [ABANDONNEE, EN_COURS],
            VALIDEE: [VALIDEE, FIGEE],
        }
        if statut in STATUS:
            for s in evol_statut[statut]:
                self.cb_nouveau_statut.addItem(s)

    def set_version(self, version: int):
        """
        Query to the database regarding version to get all attributes
        """
        self.id_version = version
        query = """
            select pluiv_v.id_version, pluiv_v.intitule, pluiv_vls.libelle as statut, pluiv_v.date_init, pluiv_v.date_fin, pluiv_vm.id_version as version_mere, pluiv_v.commentaires
            from "{0}".v_pluiv_versions pluiv_v
            join "{0}".pluiv_versionlibellestatut pluiv_vls on statut = valeur
            left join "{0}".v_pluiv_versions pluiv_vm on pluiv_vm.id_version = pluiv_v.version_mere
            where pluiv_v.id = {1}
        """.format(
            self.db_schema_name, version
        )
        print(query)
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)[0]
            self.lbl_version_value.setText(result[0])
            self.lbl_statut_value.setText(result[2])
            self.edit_intitule.setText(result[1])
            self.lbl_date_init_value.setText(
                QDateTime(result[3]).toPyDateTime().strftime("%d/%m/%Y")
            )
            self.lbl_date_fin_value.setText(
                QDateTime(result[4]).toPyDateTime().strftime("%d/%m/%Y")
                if result[4] != NULL
                else ""
            )
            self.lbl_version_mere.setVisible(result[5] != NULL)
            self.lbl_version_mere_value.setVisible(result[5] != NULL)
            self.lbl_version_mere_value.setText(
                str(result[5]) if result[5] != NULL else ""
            )
            self.edit_commentaires.setText(result[6] if result[6] != NULL else "")
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        self.enable_button()
        # Read only quand on change de version
        self.read_mode = True
        self.set_read_only_widgets(self.read_mode)

    def get_id_version(self):
        """Return id_version in pluiv_versions table regarding id"""
        query = 'select id_version from "{}".v_pluiv_versions where id = {}'.format(
            self.db_schema_name, self.id_version
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)
            return result[0][0]
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        return None

    def get_version_status(self, version):
        """Return status of the version"""

        query = """
            (SELECT pv.id, pv.id_version, pv.intitule, pvls.libelle
            FROM {}.v_pluiv_versions pv
            JOIN {}.pluiv_versionlibellestatut pvls on pv.statut = pvls.valeur
            WHERE pv.id = {}
            )
        """.format(
            self.db_schema_name, self.db_schema_name, version
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)[0]
            return result[3]
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        return None

    def enable_button(self):
        """Enable/Disable button regarding version status"""
        # Query status to know if we can duplicate version (statut in "Référence", "Figée")
        if self.id_version is not None:
            status = self.get_version_status(self.id_version)

        self.btn_dupliquer_version.setEnabled(
            (self.id_version is not None)
            and status in ["Référence", "Figée"]
            and not self.is_procedure_terminated()
        )

    def dupliquer_version(self):
        """Run DB function 'dupliquer version'"""
        id_version = self.get_id_version()
        if id_version is None:
            self.log(
                message="Impossible de récupérer la version pour la dupliquer",
                log_level=Qgis.Critical,
                push=True,
            )
            return

        # Ask confirmation to the user to duplicate version
        response = QMessageBox().question(
            self.parent,
            "Confirmer la duplication",
            f"Êtes-vous sûr de vouloir dupliquer la version {id_version} ?",
        )

        if response == QMessageBox.Yes:
            query = "select \"{}\".qgis_dupliquer_version('{}')".format(
                self.db_schema_name, self.id_version
            )
            md = QgsProviderRegistry.instance().providerMetadata("postgres")
            conn = md.createConnection(self.db_connection_uri, {})
            try:
                result = conn.executeSql(query)
                self.log(
                    message=f"La version {id_version} a été dupliquée vers la version {result[0][0]}",
                    log_level=Qgis.Success,
                    push=True,
                )
            except QgsProviderConnectionException as e:
                self.log(
                    message=catch_postgres_exception(str(e)),
                    log_level=Qgis.Critical,
                    push=True,
                )

            self.refreshVersions.emit(self.id_version)

    def charger_version(self):
        self.loadVersion.emit(self.id_version)

    def editer_details(self):
        """Editer les détails d'une version"""
        self.read_mode = not self.read_mode

        # Enregistrement des nouvelles données quand on retourne en read only
        if self.read_mode:
            self.maj_details()
            # Set version pour actualiser les détails
            self.set_version(self.id_version)
            # Signal pour actualiser les versions dans la liste
            self.refreshVersions.emit(self.id_version)
        else:
            # Remplir la liste des statuts possibles en fonction du statut actuel
            self.fill_cb_nouveau_statut()
            # Gestion de l'interface
            self.set_read_only_widgets(self.read_mode)

    def maj_details(self):
        """Enregistrement en base des nouveaux détails de version"""
        intitule = self.edit_intitule.text()
        commentaires = self.edit_commentaires.toPlainText()
        nouveau_statut = self.cb_nouveau_statut.currentText()

        query = """
        update \"{}\".pluiv_versions
            set intitule = '{}',
                commentaires = '{}'
        where id = {}
        """.format(
            self.db_schema_name,
            str.replace(intitule, "'", "''"),
            str.replace(commentaires, "'", "''"),
            self.id_version,
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            conn.executeSql(query)
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)

        # Mise à jour du statut via la fonction en base qgis_modifier_statut_version(id_version, new_statut)
        query = """
        select \"{0}\".qgis_modifier_statut_version({1}, (select valeur from \"{0}\".pluiv_versionlibellestatut where libelle = '{2}'))
        """.format(
            self.db_schema_name,
            self.id_version,
            nouveau_statut,
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            conn.executeSql(query)
            self.log(
                message=f"Les détails de la version {self.get_id_version()} ont été mis à jour",
                log_level=Qgis.Success,
                push=True,
            )
        except QgsProviderConnectionException as e:
            self.log(
                message=catch_postgres_exception(str(e)),
                log_level=Qgis.Critical,
                push=True,
            )

    def is_procedure_terminated(self) -> bool:
        """Return true if procedure has datefin_proc != NULL"""
        query = """
            select datefin_proc from "{}".v_pluiv_procedures where procedure_plui = '{}'
        """.format(
            self.db_schema_name, self.get_procedure()
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)
            return result[0][0] != NULL
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        return False

    def get_procedure(self):
        """Return procedure_plui in pluiv_procedures table regarding version id"""
        query = 'select procedure_plui from "{}".v_pluiv_versions where id = {}'.format(
            self.db_schema_name, self.id_version
        )
        md = QgsProviderRegistry.instance().providerMetadata("postgres")
        conn = md.createConnection(self.db_connection_uri, {})
        try:
            result = conn.executeSql(query)
            return result[0][0]
        except QgsProviderConnectionException as e:
            self.log(message=e, log_level=Qgis.Critical, push=True)
        return None
