#! 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, QUrl
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.gitlab_api.custom_exceptions import UnavailableUserException
from oslandia.gitlab_api.issue import Issues
from oslandia.gitlab_api.user import UserRequestManager
from oslandia.gui.dlg_authentication import AuthenticationDialog
from oslandia.gui.dlg_create_issue import CreateIssueDialog
from oslandia.gui.dlg_plugins_browser import PluginsBrowser
from oslandia.gui.dlg_user import UserDialog
from oslandia.gui.dlg_view_issues import ViewIssueDialog
from oslandia.gui.mdl_members_manager import MemberListModelManager
from oslandia.gui.mdl_project_manager import ProjectListModelManager
from oslandia.gui.onboarding.dlg_onboarding import OnboardingDialog
from oslandia.gui.settings.dlg_settings import PlgOptionsFactory
from oslandia.gui.task_gitlab_notification import GitLabNotificationManager
from oslandia.newsfeed.dck_news_feed import RssWidget
from oslandia.newsfeed.rss_reader import RssReader
from oslandia.toolbelt import PlgLogger, PlgOptionsManager
from oslandia.toolbelt.network_manager import NetworkRequestsManager
from oslandia.toolbelt.project_manager import ProjectManager
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
        self.ntwk_manager = NetworkRequestsManager()

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

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

        # -- Submodules
        self.plg_settings = PlgOptionsManager()
        self.project_manager = ProjectManager()
        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)

        # plugin settings
        self.toolbar_oslandia: QToolBar | None = None

        self.gitlab_notification_manager = None

    def initGui(self):
        """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(
                self.ntwk_manager.open_url, QUrl(__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(
            QgsApplication.getThemeIcon("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("RSS"),
            self.iface.mainWindow(),
        )

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

        # -- Menu
        self.iface.addPluginToMenu(__title__, self.action_connect)
        self.iface.addPluginToMenu(__title__, self.action_user)
        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_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_user)
        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)

        # 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(
                self.ntwk_manager.open_url, QUrl(__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 UnavailableUserException
            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 UnavailableUserException:
            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

        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 self.toolbar_oslandia:
            self.toolbar_oslandia.deleteLater()

    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.
        """
        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()

    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.post_ui_init()
            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 = PluginsBrowser()

        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 self.wdg_rss is None:
            self.wdg_rss = RssWidget()
            self.iface.addDockWidget(
                Qt.DockWidgetArea.RightDockWidgetArea, self.wdg_rss
            )

        self.wdg_rss.show()

    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
