#! python3  # noqa: E265

"""
Main plugin module.
"""

# standard
from functools import partial
from pathlib import Path
from typing import Optional

# PyQGIS
from qgis.core import Qgis, QgsApplication, QgsExpression, QgsSettings
from qgis.gui import QgisInterface
from qgis.PyQt.QtCore import QCoreApplication, QLocale, QTranslator, QUrl
from qgis.PyQt.QtGui import QDesktopServices, QIcon
from qgis.PyQt.QtWidgets import QAction

# QSFCGAL
from qsfcgal.__about__ import (
    DIR_PLUGIN_ROOT,
    __icon_path__,
    __title__,
    __uri_homepage__,
)

# project
from qsfcgal.gui.dlg_settings import PlgOptionsFactory
from qsfcgal.toolbelt.log_handler import PlgLogger

# conditional imports
try:
    # Expression
    from qsfcgal.expression.cg_alpha_shapes import cg_alpha_shapes
    from qsfcgal.expression.cg_approximate_medial_axis import cg_approximate_medial_axis
    from qsfcgal.expression.cg_convexhull import cg_convexhull
    from qsfcgal.expression.cg_difference import cg_difference
    from qsfcgal.expression.cg_force_lhr import cg_force_lhr
    from qsfcgal.expression.cg_force_rhr import cg_force_rhr
    from qsfcgal.expression.cg_intersection import cg_intersection
    from qsfcgal.expression.cg_line_sub_string import cg_line_sub_string
    from qsfcgal.expression.cg_minkowski_sum import cg_minkowski_sum
    from qsfcgal.expression.cg_offset_polygon import cg_offset_polygon
    from qsfcgal.expression.cg_optimal_alpha_shapes import cg_optimal_alpha_shapes
    from qsfcgal.expression.cg_straight_skeleton import cg_straight_skeleton
    from qsfcgal.expression.cg_triangulate_2dz import cg_triangulate_2dz
    from qsfcgal.expression.cg_union import cg_union
    from qsfcgal.processing import QsfcgalProvider

    # Provider processing
    PlgLogger.log(message="Dependencies PySFCGAL loaded.")
    EXTERNAL_DEPENDENCIES_AVAILABLE = True

except ImportError:
    PlgLogger.log(
        message="Error: importing pysfcgal, please check your dependencies.",
        log_level=Qgis.MessageLevel.Critical,
        push=True,
    )
    EXTERNAL_DEPENDENCIES_AVAILABLE = False


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


class QsfcgalPlugin:
    def __init__(self, iface: QgisInterface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class which \
        provides the hook by which you can manipulate the QGIS application at run time.
        :type iface: QgsInterface
        """
        self.iface = iface
        self.log = PlgLogger().log

        # translation
        # initialize the locale
        self.locale: str = QgsSettings().value("locale/userLocale", QLocale().name())[
            0:2
        ]
        locale_path: Path = (
            DIR_PLUGIN_ROOT
            / "resources"
            / "i18n"
            / f"{__title__.lower()}_{self.locale}.qm"
        )
        self.log(
            message=f"Translation: {self.locale}, {locale_path}",
            log_level=Qgis.MessageLevel.NoLevel,
        )
        if locale_path.exists():
            self.translator = QTranslator()
            self.translator.load(str(locale_path.resolve()))
            QCoreApplication.installTranslator(self.translator)

    def initGui(self) -> None:
        """Set up plugin UI elements."""

        # settings page within the QGIS preferences menu
        self.options_factory = PlgOptionsFactory()
        self.iface.registerOptionsWidgetFactory(self.options_factory)

        # -- Actions
        self.action_help = QAction(
            QgsApplication.getThemeIcon("mActionHelpContents.svg"),
            self.tr("Help"),
            self.iface.mainWindow(),
        )
        self.action_help.triggered.connect(
            partial(QDesktopServices.openUrl, QUrl(__uri_homepage__))
        )

        self.action_settings = QAction(
            QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
            self.tr("Settings"),
            self.iface.mainWindow(),
        )
        self.action_settings.triggered.connect(
            lambda: self.iface.showOptionsDialog(
                currentPage="mOptionsPage{}".format(__title__)
            )
        )

        # -- Menu
        self.iface.addPluginToMenu(__title__, self.action_settings)
        self.iface.addPluginToMenu(__title__, self.action_help)

        # -- Help menu
        self.action_help_plugin_menu_documentation: Optional[QAction] = None
        if not self.check_dependencies():
            return

        # documentation
        self.iface.pluginHelpMenu().addSeparator()
        self.action_help_plugin_menu_documentation = QAction(
            QIcon(str(__icon_path__)),
            f"{__title__} - Documentation",
            self.iface.mainWindow(),
        )
        self.action_help_plugin_menu_documentation.triggered.connect(
            partial(QDesktopServices.openUrl, QUrl(__uri_homepage__))
        )

        self.iface.pluginHelpMenu().addAction(
            self.action_help_plugin_menu_documentation
        )

        self.initProcessing()
        # Expression
        QgsExpression.registerFunction(cg_straight_skeleton)
        QgsExpression.registerFunction(cg_convexhull)
        QgsExpression.registerFunction(cg_difference)
        QgsExpression.registerFunction(cg_intersection)
        QgsExpression.registerFunction(cg_union)
        QgsExpression.registerFunction(cg_offset_polygon)
        QgsExpression.registerFunction(cg_minkowski_sum)
        QgsExpression.registerFunction(cg_optimal_alpha_shapes)
        QgsExpression.registerFunction(cg_alpha_shapes)
        QgsExpression.registerFunction(cg_approximate_medial_axis)
        QgsExpression.registerFunction(cg_force_lhr)
        QgsExpression.registerFunction(cg_force_rhr)
        QgsExpression.registerFunction(cg_line_sub_string)
        QgsExpression.registerFunction(cg_triangulate_2dz)

    def initProcessing(self):
        self.provider = QsfcgalProvider()
        QgsApplication.processingRegistry().addProvider(self.provider)

    def tr(self, message: str) -> str:
        """Get the translation for a string using Qt translation API.

        :param message: string to be translated.
        :type message: str

        :returns: Translated version of message.
        :rtype: str
        """
        return QCoreApplication.translate(self.__class__.__name__, message)

    def unload(self):
        """Cleans up when plugin is disabled/uninstalled."""
        # -- Clean up menu
        self.iface.removePluginMenu(__title__, self.action_help)
        self.iface.removePluginMenu(__title__, self.action_settings)

        # -- Clean up preferences panel in QGIS settings
        self.iface.unregisterOptionsWidgetFactory(self.options_factory)

        # remove from QGIS help/extensions menu
        if self.action_help_plugin_menu_documentation:
            self.iface.pluginHelpMenu().removeAction(
                self.action_help_plugin_menu_documentation
            )

        # -- Unregister processing
        try:  # in case the plugin crashed before provider registration
            QgsApplication.processingRegistry().removeProvider(self.provider)
        except AttributeError:
            pass

        # Expression
        QgsExpression.unregisterFunction("cg_straight_skeleton")
        QgsExpression.unregisterFunction("cg_convexhull")
        QgsExpression.unregisterFunction("cg_difference")
        QgsExpression.unregisterFunction("cg_intersection")
        QgsExpression.unregisterFunction("cg_union")
        QgsExpression.unregisterFunction("cg_offset_polygon")
        QgsExpression.unregisterFunction("cg_minkowski_sum")
        QgsExpression.unregisterFunction("cg_optimal_alpha_shapes")
        QgsExpression.unregisterFunction("cg_alpha_shapes")
        QgsExpression.unregisterFunction("cg_approximate_medial_axis")
        QgsExpression.unregisterFunction("cg_force_lhr")
        QgsExpression.unregisterFunction("cg_force_rhr")
        QgsExpression.unregisterFunction("cg_line_sub_string")
        QgsExpression.unregisterFunction("triangulate_2dz")

        # remove actions
        del self.action_settings
        del self.action_help

    def run(self):
        """Main process.

        :raises Exception: if there is no item in the feed
        """
        try:
            self.log(
                message=self.tr("Everything ran OK."),
                log_level=Qgis.MessageLevel.Success,
                push=False,
            )
        except Exception as err:
            self.log(
                message=self.tr("Houston, we've got a problem: {}".format(err)),
                log_level=Qgis.MessageLevel.Critical,
                push=True,
            )

    def check_dependencies(self) -> bool:
        """Check if all dependencies are satisfied. If not, warn the user and disable plugin.

        :return: dependencies status
        :rtype: bool
        """
        # if import failed
        if not EXTERNAL_DEPENDENCIES_AVAILABLE:
            msg_disable = self.tr(
                "Plugin disabled. Please install all dependencies and then restart QGIS."
                " Refer to the documentation for more information."
            )
            self.log(
                message=msg_disable,
                log_level=Qgis.MessageLevel.Critical,
                push=True,
                duration=60,
                button=True,
                button_connect=partial(
                    QDesktopServices.openUrl,
                    QUrl(f"{__uri_homepage__}/usage/installation.html"),
                ),
            )

            return False
        else:
            self.log(
                message=self.tr("Dependencies satisfied"),
                log_level=Qgis.MessageLevel.Success,
            )
            return True
