#! python3  # noqa: E265

"""
    Plugin settings form integrated into QGIS 'Options' menu.
"""

# standard
import platform
from functools import partial
from pathlib import Path
from urllib.parse import quote

# PyQGIS
from qgis.core import Qgis, QgsApplication
from qgis.gui import QgsOptionsPageWidget, QgsOptionsWidgetFactory
from qgis.PyQt import uic
from qgis.PyQt.Qt import QUrl
from qgis.PyQt.QtCore import pyqtSignal
from qgis.PyQt.QtGui import QDesktopServices, QIcon
from qgis.PyQt.QtWidgets import QButtonGroup, QMessageBox

# project
from CityForge.__about__ import (
    __icon_path__,
    __title__,
    __uri_homepage__,
    __uri_tracker__,
    __version__,
)
from CityForge.toolbelt import PlgLogger, PlgOptionsManager
from CityForge.toolbelt.check_install import (
    CheckInstall,
    ConfigurationWarning,
    GeoflowError,
    InvalidConfigurationError,
)
from CityForge.tr import Translatable

# ############################################################################
# ########## Globals ###############
# ##################################


FORM_CLASS, _ = uic.loadUiType(Path(__file__).parent / f"{Path(__file__).stem}.ui")


# ############################################################################
# ########## Classes ###############
# ##################################


class ConfigOptionsPage(FORM_CLASS, QgsOptionsPageWidget, Translatable):
    """
    Dialog settings class
    """

    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        """Constructor."""
        super().__init__(parent)
        self.log = PlgLogger().log
        self.plg_settings = PlgOptionsManager()

        # load UI and set objectName
        self.setupUi(self)
        self.setObjectName("mOptionsPage{}".format(__title__))

        self.group = QButtonGroup()
        self.group.setExclusive(True)
        self.group.addButton(self.rdbtn_executable)
        self.group.addButton(self.rdbtn_docker)

        report_context_message = quote(
            "> Reported from plugin settings\n\n"
            f"- operating system: {platform.system()} "
            f"{platform.release()}_{platform.version()}\n"
            f"- QGIS: {Qgis.QGIS_VERSION}"
            f"- plugin version: {__version__}\n"
        )

        # header
        self.lbl_title.setText(f"{__title__} - Version {__version__}")
        self.lbl_version_saved_value.setText(__version__)

        # load previously saved settings
        self.load_settings()

        # customization
        self.btn_help.setIcon(QIcon(QgsApplication.iconPath("mActionHelpContents.svg")))
        self.btn_help.pressed.connect(
            partial(QDesktopServices.openUrl, QUrl(__uri_homepage__))
        )

        self.btn_report.setIcon(
            QIcon(QgsApplication.iconPath("console/iconSyntaxErrorConsole.svg"))
        )

        self.btn_report.pressed.connect(
            partial(
                QDesktopServices.openUrl,
                QUrl(
                    f"{__uri_tracker__}new?issuable_template=bug_report&"
                    "issue[title]=[BUG]&"
                    f"issue[description]={report_context_message}"
                ),
            )
        )
        self.btn_exe_help.setIcon(
            QIcon(QgsApplication.iconPath("console/mActionHelpContents.svg"))
        )

        self.rdbtn_docker.toggled.connect(self.versiondocker)
        self.rdbtn_executable.toggled.connect(self.versionwindows)
        self.psb_test_installation.clicked.connect(self.checkInstall)

        self.btn_exe_help.pressed.connect(
            partial(
                QDesktopServices.openUrl,
                QUrl(f"{__uri_homepage__}/usage/installation.html"),
            )
        )

    def apply(self):
        """Called to permanently apply the settings shown in the options page (e.g. \
        save them to QgsSettings objects). This is usually called when the options \
        dialog is accepted."""
        settings = self.plg_settings.get_plg_settings()

        # misc
        settings.debug_mode = self.opt_debug.isChecked()
        settings.geoflow_install = self.geoflow_install
        settings.docker_path = self.filewdg_docker_path.filePath()
        settings.executable_path = self.filewdg_geof_path.filePath()
        settings.version = __version__

        # dump new settings into QgsSettings
        self.plg_settings.save_from_object(settings)

        if __debug__:
            self.log(
                message="DEBUG - Settings successfully saved.",
                log_level=4,
            )

    def load_settings(self):
        """Load options from QgsSettings into UI form."""
        settings = self.plg_settings.get_plg_settings()

        # global
        self.opt_debug.setChecked(settings.debug_mode)
        self.filewdg_docker_path.setFilePath(settings.docker_path)
        self.filewdg_geof_path.setFilePath(settings.executable_path)
        self.geoflow_install = settings.geoflow_install

        self.rdbtn_docker.setChecked(settings.geoflow_install == "docker")
        self.rdbtn_executable.setChecked(settings.geoflow_install == "executable")
        self.versiondocker()
        self.versionwindows()
        self.apply()
        return settings

    def versiondocker(self):
        if self.rdbtn_docker.isChecked():
            self.geoflow_install = "docker"
            self.filewdg_docker_path.setEnabled(True)
            self.filewdg_geof_path.setEnabled(False)

    def versionwindows(self):
        if self.rdbtn_executable.isChecked():
            self.geoflow_install = "executable"
            self.filewdg_docker_path.setEnabled(False)
            self.filewdg_geof_path.setEnabled(True)

    def checkInstall(self):
        c = CheckInstall()
        try:
            if self.rdbtn_docker.isChecked():
                c.check_docker(self.filewdg_docker_path.filePath())
            else:
                c.check_geof(self.filewdg_geof_path.filePath())

            c.check_reconstruction(
                self.geoflow_install,
                self.filewdg_docker_path.filePath(),
                self.filewdg_geof_path.filePath(),
            )

            PlgLogger.log(
                "The sample CityJSON file was successfully generated.",
                log_level=3,
                push=True,
            )
            QMessageBox.information(
                self,
                self.tr("Geoflow settings"),
                self.tr("The sample CityJSON file was successfully generated."),
            )
        except InvalidConfigurationError as e:
            QMessageBox.critical(
                self,
                self.tr("Settings error"),
                str(e),
            )
        except GeoflowError as e:
            QMessageBox.critical(
                self,
                self.tr("Geoflow error"),
                str(e)
                + "\n"
                + self.tr("You might have more details in QGIS Messages panel")
                + self.tr("You can report a bug on CityForge with details."),
            )
        except ConfigurationWarning as e:
            QMessageBox.information(
                self,
                self.tr("Warning"),
                self.tr("The sample CityJSON file was successfully generated.")
                + "\n"
                + str(e),
            )
        except Exception as e:
            QMessageBox.critical(
                self,
                self.tr("Error"),
                str(e)
                + "\n"
                + self.tr("You might have more details in QGIS Messages panel"),
            )
            return

    def closeEvent(self, event):
        """Map on plugin close.

        :param event: [description]
        :type event: [type]
        """
        self.closingPlugin.emit()
        event.accept()


class PlgOptionsFactory(QgsOptionsWidgetFactory):
    """Factory for options widget."""

    def __init__(self):
        """Constructor."""
        super().__init__()

    def icon(self) -> QIcon:
        """Returns plugin icon, used to as tab icon in QGIS options tab widget.

        :return: _description_
        :rtype: QIcon
        """
        return QIcon(str(__icon_path__))

    def createWidget(self, parent):
        return ConfigOptionsPage(parent)

    def title(self) -> str:
        """Returns plugin title, used to name the tab in QGIS options tab widget.

        :return: plugin title from about module
        :rtype: str
        """
        return __title__

    def helpId(self) -> str:
        """Returns plugin help URL.

        :return: plugin homepage url from about module
        :rtype: str
        """
        return __uri_homepage__
