#! python3  # noqa: E265

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

# standard
import platform
from collections.abc import Callable
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.QtCore import QUrl
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QMessageBox, QWidget

# project
from oslandia.__about__ import (
    __icon_path__,
    __title__,
    __uri_homepage__,
    __uri_tracker__,
    __version__,
)
from oslandia.constants import UTM_CONTENT_SETTINGS
from oslandia.gitlab_api.custom_exceptions import UnavailableUserException
from oslandia.gitlab_api.user import UserRequestManager
from oslandia.toolbelt import PlgLogger, PlgOptionsManager
from oslandia.toolbelt.cache_manager import CacheManager
from oslandia.toolbelt.network_manager import NetworkRequestsManager
from oslandia.toolbelt.preferences import PlgSettingsStructure


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


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}\n"
    f"- plugin version: {__version__}\n"
)

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


class ConfigOptionsPage(QgsOptionsPageWidget):
    """Settings form embedded into QGIS 'options' menu."""

    def __init__(self, parent: QWidget) -> None:
        """Settings dialog constructor.

        :param QgsOptionsPageWidget parent: base class for widgets for pages included
            in the options dialog.
        """
        super().__init__(parent)
        self.log: Callable = PlgLogger().log
        self.plg_settings = PlgOptionsManager()
        self.plg_settings_get: PlgSettingsStructure = (
            PlgOptionsManager.get_plg_settings()
        )
        self.cache_manager = CacheManager(".oslandia", "cache")
        self.ntwk_manager = NetworkRequestsManager()

        # load UI and set objectName
        uic.loadUi(Path(__file__).parent / f"{Path(__file__).stem}.ui", self)
        self.setObjectName(f"mOptionsPage{__title__}")
        self.initGui()

    def initGui(self) -> None:  # noqa: N802
        """Set up UI elements."""
        # header
        self.lbl_title.setText(f"{__title__} - Version {__version__}")

        # -- Support customers group
        self.btn_check_authorization.pressed.connect(self.on_check_authorization)
        self.btn_check_authorization.setIcon(
            QIcon(":/images/themes/default/repositoryDisabled.svg")
        )
        self.btn_authentication_help.setIcon(
            QIcon(QgsApplication.iconPath("mActionHelpContents.svg"))
        )
        self.btn_authentication_help.pressed.connect(
            partial(
                self.ntwk_manager.open_url,
                QUrl(f"{__uri_homepage__}usage/authentication.html"),
                UTM_CONTENT_SETTINGS,
            )
        )

        # -- Miscellaneous group
        self.btn_help.setIcon(QIcon(QgsApplication.iconPath("mActionHelpContents.svg")))
        self.btn_help.pressed.connect(
            partial(
                self.ntwk_manager.open_url, QUrl(__uri_homepage__), UTM_CONTENT_SETTINGS
            )
        )
        self.btn_report.setIcon(
            QIcon(QgsApplication.iconPath("console/iconSyntaxErrorConsole.svg"))
        )
        self.btn_report.pressed.connect(
            partial(
                self.ntwk_manager.open_url,
                QUrl(
                    f"{__uri_tracker__}/new?issuable_template=bug_report&"
                    "issue[title]=[BUG]&"
                    f"issue[description]={report_context_message}"
                ),
                UTM_CONTENT_SETTINGS,
            )
        )
        self.btn_clear_cache.setIcon(
            QIcon(":images/themes/default/console/iconClearConsole.svg")
        )
        self.btn_clear_cache.pressed.connect(partial(self.cache_manager.clear_cache))

        self.btn_reset.setIcon(QIcon(QgsApplication.iconPath("mActionUndo.svg")))
        self.btn_reset.pressed.connect(self.on_reset_settings)

        self.opt_debug.clicked.connect(self._debug_opt_changed)

        # load previously saved settings
        self.load_settings()

    def _debug_opt_changed(self) -> None:
        """Enable experimental feature depending on OS and debug mode"""
        debug_mode = self.opt_debug.isChecked()
        if debug_mode:
            self.wdg_settings_experimental_features.opt_enable_user_selection_screenshot.setEnabled(
                True
            )
        elif platform.system() != "Windows":
            self.wdg_settings_experimental_features.opt_enable_user_selection_screenshot.setChecked(
                False
            )
            self.wdg_settings_experimental_features.opt_enable_user_selection_screenshot.setEnabled(
                False
            )

        # Enable smaller notification frequency in debug mode
        self.wdg_settings_notifications.sbx_notification_frequency.setMinimum(
            5 if debug_mode else 30
        )

    def apply(self) -> None:
        """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: PlgSettingsStructure = self.plg_settings.get_plg_settings()

        # Authentication
        settings.authentification_config_id = self.acs_auth_select.configId()

        # Misc
        settings.debug_mode = self.opt_debug.isChecked()
        settings.enable_utm = self.chb_utm.isChecked()
        settings.version = __version__

        # Onboarding
        settings.run_onboarding = self.chb_trigger_onboarding.isChecked()

        self.plg_settings.save_from_object(settings)

        # Widget save in settings
        self.wdg_issue_context_auth.save_settings()
        self.wdg_settings_rss.save_settings()
        self.wdg_settings_notifications.save_settings()
        self.wdg_settings_experimental_features.save_settings()

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

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

        # Auth
        self.acs_auth_select.setConfigId(settings.authentification_config_id)

        # News feed
        self.wdg_settings_rss.load_settings()

        # Notifications
        self.wdg_settings_notifications.load_settings()

        # Experimental features
        self.wdg_settings_experimental_features.load_settings()

        # Misc
        self.opt_debug.setChecked(settings.debug_mode)
        self.chb_utm.setChecked(settings.enable_utm)
        self.lbl_version_saved_value.setText(settings.version)

        # on-boarding
        self.chb_trigger_onboarding.setChecked(settings.run_onboarding)

        # Issues
        self.wdg_issue_context_auth.load_settings()

    def on_reset_settings(self) -> None:
        """Reset settings to default values (set in preferences.py module)."""
        default_settings = PlgSettingsStructure()

        # dump default settings into QgsSettings
        self.plg_settings.save_from_object(default_settings)

        # update the form
        self.load_settings()

    def on_check_authorization(self) -> None:
        """Check connection by getting user information from API gitlab."""
        config_id = self.acs_auth_select.configId()
        # Check connection to API by getting token user information
        try:
            manager = UserRequestManager()
            manager.plg_settings.authentification_config_id = config_id
            user = manager.get_current_user()

            self.btn_check_authorization.setIcon(
                QIcon(":/images/themes/default/repositoryConnected.svg")
            )
            self.btn_check_authorization.setToolTip("Connection OK")

            QMessageBox.information(
                self,
                self.tr("Welcome"),
                self.tr("Welcome {}!".format(user.name)),
            )

        except UnavailableUserException as exc:
            self.btn_check_authorization.setIcon(
                QIcon(":/images/themes/default/repositoryUnavailable.svg")
            )
            self.btn_check_authorization.setToolTip(str(exc))


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

    def __init__(self) -> None:
        """Constructor."""
        super().__init__()

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

        :return: Qt icon
        :rtype: QIcon
        """
        return QIcon(str(__icon_path__))

    def createWidget(self, parent: QWidget) -> ConfigOptionsPage:  # noqa: N802
        """Create settings widget.

        :param parent: Qt parent where to include the options page.
        :type parent: QObject

        :return: options page for tab widget
        :rtype: ConfigOptionsPage
        """
        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:  # noqa: N802
        """Returns plugin help URL.

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