from abc import abstractmethod
from typing import Any

from qgis.core import QgsTask
from qgis.PyQt.QtCore import QObject, Qt
from qgis.PyQt.QtGui import QColor

from oslandia.gitlab_api.gitlab_object import PageLoadResult
from oslandia.gitlab_api.issue import Issues, IssuesRequestManager
from oslandia.gui.mdl_gitlab_object import AbstractGitlabObjectModel
from oslandia.toolbelt.qt_utils import localize_qdatetime, to_qdatetime


class IssuesListModel(AbstractGitlabObjectModel):
    CREATED_DATE_COL = 0
    UPDATED_DATE_COL = 1
    NAME_COL = 2
    STATE_COL = 3
    WEB_URL_COL = 4
    TOTAL_TIME_SPENT = 5
    DESCRIPTION_COL = 6

    def __init__(self, parent: QObject = 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"),
        ]

    @abstractmethod
    def _object_data_for_role(
        self, gitlab_object: Any, col: int, role=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: _type_, 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 "",
            }
            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,
                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,
            }
            return res.get(col, None)

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

        return None

    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 = IssuesRequestManager()
            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
