"""A text editor that automatically upload file to gitlab at drop"""

# standard
import tempfile
from pathlib import Path

# PyQGIS
from qgis.core import QgsApplication
from qgis.gui import QgsGui
from qgis.PyQt.QtCore import QMimeData
from qgis.PyQt.QtGui import QIcon, QImage, QPixmap
from qgis.PyQt.QtWidgets import QAction, QFileDialog, QWidget
from qgis.utils import iface

from oslandia.gui.helpdesk.auto_resizing_text_edit import AutoResizingTextEdit
from oslandia.gui.helpdesk.screenshot_maker import take_screenshot

# project
from oslandia.helpdesk.upload import UploadRequestManager
from oslandia.toolbelt.preferences import PlgOptionsManager, PlgSettingsStructure
from oslandia.toolbelt.qt_utils import (
    _get_local_files_from_mimedata,
    current_datetime_str,
)


class GitLabTextEdit(AutoResizingTextEdit):
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)

        self._project_id = None
        self.setAcceptDrops(True)

        self._insert_separator_in_edit_action()
        attach_file_action = QAction(
            QIcon(QgsApplication.iconPath("mActionEditInsertLink.svg")),
            self.tr("Attach file"),
        )
        attach_file_action.setToolTip(self.tr("Attach file or image."))
        attach_file_action.triggered.connect(self._insert_file)
        self._edit_actions.append(attach_file_action)

        canvas_screenshot_action = QAction(
            QgsApplication.getThemeIcon("mActionSaveMapAsImage.svg"),
            self.tr("Canvas screenshot."),
        )
        canvas_screenshot_action.setToolTip(self.tr("Canvas screenshot."))
        canvas_screenshot_action.triggered.connect(self._canvas_screenshot)
        self._edit_actions.append(canvas_screenshot_action)

        self.take_screenshot_action = QAction(
            QIcon(":images/themes/default/propertyicons/overlay.svg"),
            self.tr("Take screenshot."),
        )
        self.take_screenshot_action.setToolTip(self.tr("Take screenshot."))
        self.take_screenshot_action.triggered.connect(self._take_screenshot)
        self._edit_actions.append(self.take_screenshot_action)

        self.update_with_plg_settings()

        instance_gui = QgsGui.instance()
        instance_gui.optionsChanged.connect(self.update_with_plg_settings)

    def _insert_file(self) -> None:
        """Select file and insert to edit widget"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, self.tr("Select file to upload")
        )
        if file_path:
            self.insert_file(file_path)

    def _canvas_screenshot(self) -> None:
        """Print current canvas to QPixmap and insert it"""
        canvas = iface.mapCanvas()
        if canvas:
            tmp_path = self._temp_file_name(".png")
            canvas.saveAsImage(str(tmp_path))
            self.insert_file(
                tmp_path,
                self.tr("QGIS canvas added {}").format(current_datetime_str("long")),
            )

    def _take_screenshot(self) -> None:
        """Take a screenshot from user selection and insert it"""
        pixmap = take_screenshot()
        if pixmap:
            self._insert_pixmap(
                pixmap,
                self.tr("Screenshot added {}").format(current_datetime_str("long")),
            )

    @staticmethod
    def _temp_file_name(suffix: str) -> Path:
        """Return temporary file name. File is closed to be used by Qt

        :param suffix: filename suffix
        :type suffix: str
        :return: temporary file name
        :rtype: Path
        """
        # Create temporary file
        tmp_file = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
        tmp_path = tmp_file.name
        # We are only using tempfile.NamedTemporaryFile to get a temp file path
        # Need to close to be able to save screenshot
        tmp_file.close()
        return Path(tmp_path)

    def _insert_pixmap(self, pixmap: QPixmap, insert_text: str | None) -> None:
        """Insert pixmap as file

        :param pixmap: pixmap
        :type pixmap: QPixmap
        :param insert_text: text to insert before pixmap (optional)
        :type insert_text: Optional[str]
        """
        tmp_path = self._temp_file_name(".png")
        pixmap.save(str(tmp_path), "png")
        self.insert_file(Path(tmp_path), insert_text)

    def set_project_id(self, _project_id: int) -> None:
        """Define project_id needed for image displau

        :param _project_id: project id
        :type _project_id: int
        """
        self._project_id = _project_id

    def insertFromMimeData(self, source: QMimeData | None) -> None:  # noqa: N802
        """Override insertFromMimeData to paste files or images

        :param source: mime data to import
        :type source: Optional[QMimeData]
        """
        # No override if no project defined, we can't add files or images
        if not self._project_id:
            super().insertFromMimeData(source)
            return

        # Add mimedata if files or images
        source_inserted = False
        if source:
            if source.hasUrls():
                file_paths = _get_local_files_from_mimedata(source)
                for path in file_paths:
                    source_inserted = True
                    self.insert_file(path)
            if source.hasImage():
                source_inserted = True
                image: QImage = source.imageData()
                self._insert_pixmap(
                    QPixmap.fromImage(image),
                    self.tr("Image added {}").format(current_datetime_str("long")),
                )
        if not source_inserted:
            super().insertFromMimeData(source)

    def canInsertFromMimeData(self, source: QMimeData | None) -> bool:  # noqa: N802
        """Override canInsertFromMimeData to enable action for paste of files or images

        :param source: _description_
        :type source: Optional[QMimeData]
        :return: True if mimedata can be inserted, False otherwise
        :rtype: bool
        """
        # No override if no project defined, we can't add files or images
        if not self._project_id:
            return super().canInsertFromMimeData(source)

        if source:
            if source.hasUrls() and len(_get_local_files_from_mimedata(source)) != 0:
                return True
            if source.hasImage():
                return True
        return super().canInsertFromMimeData(source)

    def insert_file(self, file_path: Path, insert_text: str | None = None) -> None:
        """Insert file in document.
        Upload file to project and insert markdown string

        :param file_path: file path
        :type file_path: Path
        :param insert_text: text to insert before file (optional)
        :type insert_text: Optional[str]
        """
        if not self._project_id:
            return
        manager = UploadRequestManager()
        md_str = manager.create_upload(self._project_id, file_path)
        if md_str:
            if insert_text:
                self.textCursor().insertText(insert_text)
                self.textCursor().insertText("\n\n")

            self.textCursor().insertText(md_str)
            self.textCursor().insertText("\n")

    def update_with_plg_settings(self) -> None:
        """Force update of widget with new plugin settings
        Enable user selection screenshot action from settings
        """
        plg_settings = PlgOptionsManager()
        settings: PlgSettingsStructure = plg_settings.get_plg_settings()

        # Enable user selection screenshot from settings
        self.take_screenshot_action.setVisible(
            settings.enable_user_selection_screenshot
        )
