# standard
from abc import abstractmethod
from typing import Any

# PyQGIS
from qgis.core import QgsTask
from qgis.PyQt.QtCore import QObject, Qt, QVariant
from qgis.PyQt.QtGui import QColor, QIcon

# project
from oslandia.__about__ import DIR_PLUGIN_ROOT
from oslandia.gui.helpdesk.mdl_gitlab_object import AbstractGitlabObjectModel
from oslandia.helpdesk.gitlab_object import PageLoadResult
from oslandia.helpdesk.issue import Issues, IssuesRestRequestManager
from oslandia.toolbelt.qt_utils import localize_qdatetime, to_qdatetime


class IssuesListModel(AbstractGitlabObjectModel):
    """Model for displaying issues in a QGIS table view.

    :param AbstractGitlabObjectModel: Base class for GitLab object models
    """

    CREATED_DATE_COL = 0
    UPDATED_DATE_COL = 1
    NAME_COL = 2
    STATE_COL = 3
    WEB_URL_COL = 4
    TOTAL_TIME_SPENT = 5
    COMMENTS_COL = 6
    DESCRIPTION_COL = 7

    def __init__(self, parent: QObject | None = None) -> None:
        """QStandardItemModel for datastore list display.

        Args:
            parent: QObject parent
        """
        super().__init__(parent)

    @abstractmethod
    def _column_headers(self) -> list[str]:
        """Returns column header list

        :return: columns header
        :rtype: List[str]
        """
        return [
            self.tr("Created"),
            self.tr("Updated"),
            self.tr("Title"),
            self.tr("State"),
            self.tr("Link"),
            self.tr("Time spent"),
            self.tr("Comments"),
        ]

    @abstractmethod
    def _object_data_for_role(
        self,
        gitlab_object: Any,
        col: int,
        role: Qt.ItemDataRole = Qt.ItemDataRole.DisplayRole,
    ) -> Any:
        """Return qt model data for a specific column and role

        :param gitlab_object: object to display
        :type gitlab_object: Any
        :param col: column for table display
        :type col: int
        :param role: qt role, defaults to Qt.ItemDataRole.DisplayRole
        :type role: ItemDataRole, optional

        :return: qt model data
        :rtype: Any
        """
        issues: Issues = gitlab_object

        if role == Qt.ItemDataRole.DisplayRole:
            res = {
                self.NAME_COL: issues.name,
                self.CREATED_DATE_COL: to_qdatetime(issues.created_date),
                self.UPDATED_DATE_COL: to_qdatetime(issues.updated_at)
                if issues.updated_at
                else "",
                self.STATE_COL: issues.state,
                self.WEB_URL_COL: f"#{issues.iid}",
                self.DESCRIPTION_COL: issues.description,
                self.TOTAL_TIME_SPENT: issues.time_stats.human_total_time_spent
                if issues.time_stats
                else "",
                self.COMMENTS_COL: issues.user_notes_count,
            }
            return res.get(col, None)
        if role == Qt.ItemDataRole.UserRole:
            res = {
                self.NAME_COL: issues,
                self.WEB_URL_COL: issues.web_url,
            }
            return res.get(col, None)

        if role == Qt.ItemDataRole.ToolTipRole:
            res = {
                self.NAME_COL: issues.description_without_comments,
                self.CREATED_DATE_COL: localize_qdatetime(
                    to_qdatetime(issues.created_date), date_format="long"
                ),
                self.WEB_URL_COL: issues.web_url,
                self.DESCRIPTION_COL: issues.description_without_comments,
            }
            return res.get(col, None)

        if role == Qt.ItemDataRole.BackgroundRole and issues.state != "opened":
            return QColor("whitesmoke")

        return None

    def headerData(  # noqa: N802
        self,
        section: int,
        orientation: Qt.Orientation,
        role: Qt.ItemDataRole = Qt.ItemDataRole.DisplayRole,
    ) -> QIcon | str | QVariant | None:
        """Override method to customize tabwidget headers: icon, tooltip, etc.

        :param section: section number (column for Horizontal orientation,
            row for Vertical orientation)
        :type section: int
        :param orientation: orientation
        :type orientation: Qt.Orientation
        :param role: data role
        :type role: Qt.ItemDataRole.DisplayRole

        :return: header data
        :rtype: QIcon | str | QVariant | None
        """
        if orientation == Qt.Orientation.Horizontal and section in (
            self.COMMENTS_COL,
            self.TOTAL_TIME_SPENT,
        ):
            if section == self.COMMENTS_COL:
                if role == Qt.ItemDataRole.DecorationRole:
                    return QIcon(
                        str(DIR_PLUGIN_ROOT / "resources/images/helpdesk/comments.svg")
                    )
                if role == Qt.ItemDataRole.ToolTipRole:
                    return self.tr("Comments")
            elif section == self.TOTAL_TIME_SPENT:
                if role == Qt.ItemDataRole.DecorationRole:
                    return QIcon(
                        str(DIR_PLUGIN_ROOT / "resources/images/helpdesk/timer.svg")
                    )
                if role == Qt.ItemDataRole.ToolTipRole:
                    return self.tr("Time spent")

            if role == Qt.ItemDataRole.DisplayRole:
                return ""
        return super().headerData(section, orientation, role)

    def _load_task_description(self) -> str:
        """Test display in QGIS task manager for page load task

        :return: page load description
        :rtype: str
        """
        return self.tr("Load issues for project {}").format(self.obj_filter)

    @abstractmethod
    def _load_object_page(
        self, task: QgsTask | None, obj_filter: Any, page: int
    ) -> PageLoadResult | None:
        """Load issue list for a project for a specific page

        :param task: task used for load, can be None
        :type task: QgsTask, optional
        :param obj_filter: filter reprensenting project id. Must be a str
        :type obj_filter: Any
        :param page: page to load
        :type page: int
        :return: page load result, None in case of error
        :rtype: Optional[PageLoadResult]
        """
        try:
            manager = IssuesRestRequestManager()
            result = manager.get_issues_list_by_page(
                obj_filter, page=page, limit=self.nb_item_by_page
            )
            return result
        except ConnectionError:
            return None

    def get_issue_row(self, issue: Issues) -> int:
        """Return model row for an issue. -1 if issue is not available

        :param issue: issue
        :type issue: Issues

        :return: row model matching issue
        :rtype: int
        """
        for row in range(self.rowCount()):
            row_issue: Issues = self.data(
                self.index(row, self.NAME_COL), Qt.ItemDataRole.UserRole
            )
            if row_issue.id == issue.id and row_issue.project_id == issue.project_id:
                return row
        return -1
