#! python3  # noqa: E265

"""
Main plugin module.
"""

# standard
from functools import partial
from typing import TYPE_CHECKING

from qgis.core import Qgis, QgsApplication, QgsSettings
from qgis.gui import QgisInterface, QgsGui
from qgis.PyQt.QtCore import QCoreApplication, QLocale, Qt, QTranslator
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QToolBar
from qgis.utils import OverrideCursor

# project
from oslandia.__about__ import (
    DIR_PLUGIN_ROOT,
    __icon_path__,
    __title__,
    __uri_homepage__,
)
from oslandia.constants import UTM_CONTENT_HELPDESK
from oslandia.custom_exceptions import UnavailableUserError
from oslandia.gui.authentication.dlg_authentication import AuthenticationDialog
from oslandia.gui.helpdesk.dlg_create_issue import CreateIssueDialog
from oslandia.gui.helpdesk.dlg_user import UserDialog
from oslandia.gui.helpdesk.dlg_view_issues import ViewIssueDialog
from oslandia.gui.helpdesk.mdl_members_manager import MemberListModelManager
from oslandia.gui.helpdesk.mdl_project_manager import ProjectListModelManager
from oslandia.gui.helpdesk.task_gitlab_notification import GitLabNotificationManager
from oslandia.gui.newsfeed.dck_news_feed import RssDockWidget
from oslandia.gui.onboarding.dlg_onboarding import OnboardingDialog
from oslandia.gui.settings.dlg_settings import PlgOptionsFactory
from oslandia.gui.showcase.dlg_showcase import Showcase
from oslandia.helpdesk.issue import Issues
from oslandia.helpdesk.user import UserRequestManager
from oslandia.newsfeed.rss_reader import RssReader
from oslandia.showcase.showcase_manager import ShowcaseManager
from oslandia.toolbelt import PlgLogger, PlgOptionsManager
from oslandia.toolbelt.network_manager import open_url_in_browser
from oslandia.toolbelt.qt_utils import TranslatableMixin


if TYPE_CHECKING:
    from pathlib import Path


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


class OslandiaPlugin(TranslatableMixin):
    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.wizard = None
        self.log = PlgLogger().log

        # -- Dialogs
        self.dlg_auth: AuthenticationDialog | None = None
        self.dlg_onboarding: OnboardingDialog | None = None
        self.dlg_create_issue: CreateIssueDialog | None = None
        self.dlg_oslandia_projects: Showcase | None = None
        self.dlg_user: UserDialog | None = None
        self.dlg_view_issue: ViewIssueDialog | None = None

        #  -- Widgets
        self.wdg_rss: RssDockWidget | None = None

        # -- Submodules
        self.plg_settings = PlgOptionsManager()
        self.project_manager = ShowcaseManager()
        self.rss_reader = RssReader()
        self.options_factory = None

        # -- Actions
        self.action_connect = None
        self.action_create_issue = None
        self.action_help = None
        self.action_settings = None
        self.action_user = None
        self.action_view_issue = None
        self.action_onboarding = None

        # 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)

        # others
        self.toolbar_oslandia: QToolBar | None = None
        self.gitlab_notification_manager = None
        self._post_ui_init_done: bool = False

    def initGui(self) -> None:  # noqa: N802
        """Set up plugin UI elements."""
        self.iface.initializationCompleted.connect(self.post_ui_init)

        # 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(open_url_in_browser, __uri_homepage__, UTM_CONTENT_HELPDESK)
        )

        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=f"mOptionsPage{__title__}")
        )

        self.action_create_issue = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/create_issue.png")),
            self.tr("Create issue..."),
            self.iface.mainWindow(),
        )

        self.action_create_issue.triggered.connect(self._create_issue)

        self.action_view_issue = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/view_issue.png")),
            self.tr("View my helpdesk issues..."),
            self.iface.mainWindow(),
        )

        self.action_display_projects_browser = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/plugin.png")),
            self.tr("Plugins & projects showcase"),
            self.iface.mainWindow(),
        )

        self.action_view_issue.triggered.connect(self._view_issue)

        self.action_connect = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/connect.png")),
            self.tr("Connect"),
            self.iface.mainWindow(),
        )

        self.action_connect.triggered.connect(self.display_auth)

        self.action_user = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/user.svg")),
            self.tr("My Oslandia account"),
            self.iface.mainWindow(),
        )

        self.action_user.triggered.connect(self.display_user)

        self.action_rss = QAction(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/images/rss.svg")),
            self.tr("News feed"),
            self.iface.mainWindow(),
        )
        self.action_rss.setCheckable(True)

        self.action_rss.triggered.connect(self.display_rss)

        # -- Menu
        self.iface.addPluginToMenu(__title__, self.action_connect)
        self.iface.addPluginToMenu(__title__, self.action_create_issue)
        self.iface.addPluginToMenu(__title__, self.action_view_issue)
        self.iface.addPluginToMenu(__title__, self.action_display_projects_browser)
        self.iface.addPluginToMenu(__title__, self.action_rss)
        self.iface.addPluginToMenu(__title__, self.action_user)
        self.iface.addPluginToMenu(__title__, self.action_settings)
        self.iface.addPluginToMenu(__title__, self.action_help)

        # -- Toolbar
        if self.toolbar_oslandia is None:
            self.toolbar_oslandia = QToolBar("OslandiaToolbar", self.iface.mainWindow())
            self.toolbar_oslandia.setObjectName("OslandiaToolbar")
        self.iface.addToolBar(self.toolbar_oslandia)
        self.toolbar_oslandia.addAction(self.action_connect)
        self.toolbar_oslandia.addAction(self.action_view_issue)
        self.toolbar_oslandia.addAction(self.action_create_issue)
        self.toolbar_oslandia.addAction(self.action_display_projects_browser)
        self.toolbar_oslandia.addAction(self.action_rss)
        self.toolbar_oslandia.addAction(self.action_user)

        # 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(open_url_in_browser, __uri_homepage__, UTM_CONTENT_HELPDESK)
        )

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

        self.action_display_projects_browser.triggered.connect(
            self.display_oslandia_plugins_dialog
        )

        self.action_user.setVisible(False)
        self.action_create_issue.setVisible(False)
        self.action_view_issue.setVisible(False)
        instance_gui = QgsGui.instance()
        instance_gui.optionsChanged.connect(self._check_available_actions)

        if self.plg_settings.get_plg_settings().debug_mode:
            self.post_ui_init()

        # On-boarding
        settings = self.plg_settings.get_plg_settings()
        if not self.action_onboarding:
            self.action_onboarding = QAction(
                QIcon(
                    str(
                        DIR_PLUGIN_ROOT
                        / "resources/images/oslandia_logo_sans_texte.svg"
                    )
                ),
                self.tr("Getting started with the Oslandia plugin"),
                self.iface.mainWindow(),
            )
        if settings.run_onboarding:
            self.toolbar_oslandia.addAction(self.action_onboarding)
            self.action_onboarding.setVisible(True)
            self.action_onboarding.triggered.connect(self.run_onboarding)
            self.set_actions_enabled(False)

    def run_onboarding(self) -> None:
        """Display on-boarding dialog."""
        if self.dlg_onboarding is None:
            self.dlg_onboarding = OnboardingDialog()
            self.dlg_onboarding.accepted.connect(self.on_close_onboarding)

        self.dlg_onboarding.show()

    def on_close_onboarding(self):
        """Enables the interface when onboarding has been closed."""
        self.set_actions_enabled(True)
        self._check_available_actions()
        self.toolbar_oslandia.removeAction(self.action_onboarding)

    def set_actions_enabled(self, activate: bool) -> None:
        """Enable or disable application actions.

        :param activate: If True, actions are enabled. If False, actions are disabled.
        :type activate: bool
        :return: None
        """
        # Enable or disable actions. Maybe we should check if widgets are visible or not
        # but it seems to work like this
        self.action_connect.setEnabled(activate)
        self.action_create_issue.setEnabled(activate)
        self.action_display_projects_browser.setEnabled(activate)
        self.action_rss.setEnabled(activate)
        self.action_user.setEnabled(activate)
        self.action_view_issue.setEnabled(activate)

    def _check_available_actions(self) -> None:
        """Define available actions from current connection"""
        # Check connection to API by getting token user information
        try:
            if not self.plg_settings.get_plg_settings().authentification_config_id:
                self.log(
                    message=self.tr(
                        "Authentication configuration ID is not set in settings. "
                        "Please use the authentication dialog to pair your GitLab account."
                    )
                )
                raise UnavailableUserError
            manager = UserRequestManager()
            manager.get_current_user()
            self.action_connect.setVisible(False)
            self.action_user.setVisible(True)
            self.action_create_issue.setVisible(True)
            self.action_view_issue.setVisible(True)

            # Start notification manager
            if self.gitlab_notification_manager:
                self.gitlab_notification_manager.start()

        except UnavailableUserError:
            self.action_connect.setVisible(True)
            self.action_user.setVisible(False)
            self.action_create_issue.setVisible(False)
            self.action_view_issue.setVisible(False)

            # Stop notification manager
            if self.gitlab_notification_manager:
                self.gitlab_notification_manager.stop()

    def _create_issue(self) -> None:
        """Display create issue dialog"""

        # For now use first available group
        group_id = self.plg_settings.get_plg_settings().gitlab_group_list_values[0]

        if not self.dlg_create_issue:
            self.dlg_create_issue = CreateIssueDialog(group_id, self.iface.mainWindow())
            self.dlg_create_issue.setModal(False)
        self.dlg_create_issue.show()
        self.dlg_create_issue.activateWindow()

    def _view_issue(self) -> None:
        """Display view issue dialog"""

        # Stop notification manager
        if self.gitlab_notification_manager:
            self.gitlab_notification_manager.stop()

        if not self.dlg_view_issue:
            self.dlg_view_issue = ViewIssueDialog()
            self.dlg_view_issue.setModal(False)

            # Restart notification manager when dialog is closed
            if self.gitlab_notification_manager:
                self.dlg_view_issue.finished.connect(
                    self.gitlab_notification_manager.restart
                )
        self.dlg_view_issue.show()
        self.dlg_view_issue.activateWindow()

    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)
        self.iface.removePluginMenu(__title__, self.action_connect)
        self.iface.removePluginMenu(__title__, self.action_user)
        self.iface.removePluginMenu(__title__, self.action_create_issue)
        self.iface.removePluginMenu(__title__, self.action_view_issue)
        self.iface.removePluginMenu(__title__, self.action_display_projects_browser)
        self.iface.removePluginMenu(__title__, self.action_rss)

        # -- 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
            )

        # remove actions
        del self.action_settings
        del self.action_help

        # unconnect signals
        self.iface.initializationCompleted.disconnect(self.post_ui_init)
        QgsGui.instance().optionsChanged.disconnect(self._check_available_actions)

        if self.gitlab_notification_manager is not None:
            self.gitlab_notification_manager.stop()
            self.gitlab_notification_manager.deleteLater()

        # Clear project model
        ProjectListModelManager.clear()

        # Save last viewed project
        settings = self.plg_settings.get_plg_settings()
        if self.dlg_view_issue and (
            last_project_id := self.dlg_view_issue.selected_project_id()
        ):
            settings.issue_view_last_project_id = last_project_id

        if self.dlg_view_issue:
            self.dlg_view_issue.close()
            self.dlg_view_issue.deleteLater()
            self.dlg_view_issue = None

        if self.dlg_create_issue and (
            last_project_id := self.dlg_create_issue.selected_project_id()
        ):
            settings.issue_creation_last_project_id = last_project_id

        if self.dlg_create_issue:
            self.dlg_create_issue.close()
            self.dlg_create_issue.deleteLater()
            self.dlg_create_issue = None

        self.plg_settings.save_from_object(settings)

        # remove toolbar :
        if isinstance(self.toolbar_oslandia, QToolBar):
            self.toolbar_oslandia.deleteLater()
            del self.toolbar_oslandia

    def post_ui_init(self):
        """Things to do when QGIS ui is completed, without blocking QGIS
            initialization (First Content Pain).

        Becareful, this signal is only emitted when the interface is initialized, not
        refreshed or modified. For example, it's not emitted when the plugin is
        manually activated through the extensions manager and nor when another
        processing modify the QGIS GUI.
        """
        if self._post_ui_init_done:
            return

        self.project_manager.update_json_projects()
        self.rss_reader.process()

        # Init project model for current gitlab group id
        settings = self.plg_settings.get_plg_settings()
        for group_id in settings.gitlab_group_list_values:
            ProjectListModelManager.init_model_for_group(group_id)

        self.gitlab_notification_manager = GitLabNotificationManager(
            self.iface.mainWindow(),
            issue_clicked_callable=self._issue_notification_clicked,
        )

        self._check_available_actions()
        self._post_ui_init_done = True

    def _issue_notification_clicked(self, issue: Issues) -> None:
        """Display issue after a notification is clicked

        :param issue: issue to display
        :type issue: Issues
        """
        # Display dialog
        self._view_issue()

        # Select issue
        if self.dlg_view_issue:
            self.dlg_view_issue.select_issue(issue)

    def download_icons_and_json_if_needed(self):
        """Download icons and json to cache if not exists"""

        with OverrideCursor(Qt.CursorShape.WaitCursor):
            if not self.project_manager.json_exists:
                self.project_manager.update_json_projects()
            self.project_manager.download_projects_icons_to_cache()
            self.project_manager.download_projects_images_to_cache()

    def display_auth(self) -> None:
        """Display authentication dialog to initiate log-in pairing on GitLab."""
        if self.dlg_auth is None:
            self.dlg_auth = AuthenticationDialog()
            self.dlg_auth.finished.connect(self._check_available_actions)

        self.dlg_auth.show()

    def display_oslandia_plugins_dialog(self) -> None:
        """Display instance duckdb add layer dialog"""
        self.download_icons_and_json_if_needed()

        if self.dlg_oslandia_projects is None:
            self.dlg_oslandia_projects = Showcase()

        self.dlg_oslandia_projects.show()

    def display_user(self) -> None:
        """Display user dialog."""
        if self.dlg_user is None:
            self.dlg_user = UserDialog()
            self.dlg_user.user_disconnected.connect(self._user_disconnected)

        self.dlg_user.show()

    def display_rss(self) -> None:
        """Display RSS widget."""
        if not self.action_rss.isChecked() and self.wdg_rss:
            self.wdg_rss.hide()
        else:
            if self.wdg_rss is None:
                self.wdg_rss = RssDockWidget()
                self.iface.addDockWidget(
                    Qt.DockWidgetArea.RightDockWidgetArea, self.wdg_rss
                )
                self.wdg_rss.closed.connect(self._rss_closed)
            self.wdg_rss.show()

    def _rss_closed(self) -> None:
        """RSS dialog has been closed"""
        self.action_rss.setChecked(False)

    def _user_disconnected(self) -> None:
        """Close opened dialog and clear model manager after user disconnect"""
        self._check_available_actions()
        ProjectListModelManager.clear()
        MemberListModelManager.clear()

        if self.dlg_create_issue:
            self.dlg_create_issue.close()
            del self.dlg_create_issue
            self.dlg_create_issue = None

        if self.dlg_view_issue:
            self.dlg_view_issue.close()
            del self.dlg_view_issue
            self.dlg_view_issue = None
