# standard
import json
from dataclasses import dataclass, field

# PyQGIS
from qgis.PyQt.QtCore import QByteArray, QUrl

# plugin
from oslandia.constants import UTM_CONTENT_ISSUEBROWSER
from oslandia.helpdesk.gitlab_object import (
    PageLoadResult,
    get_nb_result_and_page_from_reply,
)
from oslandia.helpdesk.markdown import append_watermark, strip_markdown_html_comments
from oslandia.toolbelt import PlgLogger, PlgOptionsManager
from oslandia.toolbelt.network_manager import NetworkRequestsManager


@dataclass
class TimeStats:
    time_estimate: int
    total_time_spent: int
    human_time_estimate: str | None
    human_total_time_spent: str | None


@dataclass
class Issues:
    """Represent a GitLab issue."""

    id: int
    iid: int
    project_id: int
    name: str
    created_date: str
    updated_at: str | None = None
    state: str = ""
    description: str = ""
    web_url: str = ""
    labels: list[str] = field(default_factory=lambda: [])
    assignee_id: int | None = None
    time_stats: TimeStats | None = None
    user_notes_count: int = 0
    # Not defined by REST API
    project_full_path: str | None = None

    @property
    def description_without_comments(self) -> str | None:
        """Description with comments removed.

        :return: description wihtout comments
        :rtype: str | None
        """
        if self.description:
            return strip_markdown_html_comments(self.description)

    def valid_for_creation(self) -> bool:
        """Check if issue is valid for creation

        :return: True if issue is valid, False otherwise
        :rtype: bool
        """
        valid = self.name.strip() != ""
        return valid


class IssuesRestRequestManager:
    MAX_LIMIT = 50

    def get_base_url(self, project_id: int) -> str:
        """
        Get base url for members of a group

        Args:
            project_id: (int) project id

        Returns: url for members info

        """
        return f"{self.plg_settings.gitlab_api_url_base}projects/{project_id}/issues"

    def __init__(self):
        """
        Helper for stored_data request

        """
        self.log = PlgLogger().log
        self.ntwk_requester_blk = NetworkRequestsManager()
        self.plg_settings = PlgOptionsManager.get_plg_settings()

    def get_issues_list(self, project_id: int) -> list[Issues]:
        """
        Get list of members

        Args:
            project_id: (int) project id

        Returns: list of available members, raise ReadStoredDataException otherwise
        """
        self.log(f"{__name__}.get_issue_list(project_id:{project_id})")

        result = []
        page_result = self.get_issues_list_by_page(project_id, 1, self.MAX_LIMIT)
        nb_page = page_result.nb_page
        result += page_result.gitlab_object_list
        for page in range(2, nb_page + 1):
            page_result = self.get_issues_list_by_page(project_id, page, self.MAX_LIMIT)
            result += page_result.gitlab_object_list

        return result

    def get_issues_list_by_page(
        self, project_id: int, page: int = 1, limit: int = MAX_LIMIT
    ) -> PageLoadResult:
        """
        Get list of issues

        Args:
            project_id: (int) project id
            page: (int) page number (start at 1)
            limit: (int)

        Returns: list of available members
        """

        # send request
        req_reply = self.ntwk_requester_blk.get_url(
            QUrl(
                f"{self.plg_settings.gitlab_api_url_base}/projects/{project_id}/issues?page={page}&per_page={limit}"
            ),
            self.plg_settings.authentification_config_id,
            return_req_reply=True,
            caller=UTM_CONTENT_ISSUEBROWSER,
        )
        if req_reply:
            nb_result, nb_page = get_nb_result_and_page_from_reply(req_reply)
            data = json.loads(req_reply.content().data().decode("utf-8"))
            return PageLoadResult(
                nb_object=nb_result,
                nb_page=nb_page,
                page=page,
                gitlab_object_list=[self._issue_from_data(p_data) for p_data in data],
            )
        return PageLoadResult(nb_object=0, nb_page=0, page=0, gitlab_object_list=[])

    def _issue_from_data(self, data: dict) -> Issues:
        """Create an issue from data dict

        :param data: input data dict
        :type data: dict
        :return: issue from data
        :rtype: Issues
        """
        return Issues(
            id=data["id"],
            iid=data["iid"],
            project_id=data["project_id"],
            name=data["title"],
            created_date=data["created_at"].strip(),
            updated_at=data["updated_at"] if data["updated_at"] else None,
            state=data["state"],
            description=data["description"],
            labels=data["labels"],
            assignee_id=(data["assignee"]["id"] if data["assignee"] else None),
            web_url=data["web_url"],
            user_notes_count=data.get("user_notes_count", 0),
            time_stats=(
                TimeStats(
                    time_estimate=data["time_stats"]["time_estimate"],
                    total_time_spent=data["time_stats"]["total_time_spent"],
                    human_time_estimate=data["time_stats"]["human_time_estimate"],
                    human_total_time_spent=data["time_stats"]["human_total_time_spent"],
                )
                if data["time_stats"]
                else None
            ),
        )

    def create_issue(self, project_id: int, issue: Issues) -> Issues:
        """
        Create an issue in a project

        Args:
            project_id: (int) project id
            issue: (Issues) issue to create
        """
        self.log(f"{__name__}.create_issue()")

        # encode data
        data = QByteArray()
        data_map = {
            "title": issue.name,
            "description": append_watermark(issue.description),
            "labels": issue.labels,
        }
        if issue.assignee_id:
            data_map["assignee_id"] = issue.assignee_id

        data.append(json.dumps(data_map).encode("utf-8"))

        req_reply = self.ntwk_requester_blk.post_url(
            url=QUrl(self.get_base_url(project_id=project_id)),
            config_id=self.plg_settings.authentification_config_id,
            data=data,
            content_type_header="application/json",
            caller=UTM_CONTENT_ISSUEBROWSER,
        )
        data = json.loads(req_reply.data().decode("utf-8"))
        return self._issue_from_data(data)

    def update_issue(self, issue: Issues) -> Issues:
        """Update issue and return updated data

        :param issue: issue to update
        :type issue: Issues
        :return: updated issue
        :rtype: Issues
        """
        self.log(f"{__name__}.update_issue()")

        # encode data
        data = QByteArray()
        data_map = {
            "id": issue.id,
            "assignee_ids": [issue.assignee_id],
            "description": issue.description,
            "title": issue.name,
            "labels": issue.labels,
        }
        data.append(json.dumps(data_map).encode("utf-8"))

        req_reply = self.ntwk_requester_blk.put_url(
            url=QUrl(f"{self.get_base_url(project_id=issue.project_id)}/{issue.iid}"),
            config_id=self.plg_settings.authentification_config_id,
            data=data,
            content_type_header="application/json",
            caller=UTM_CONTENT_ISSUEBROWSER,
        )
        data = json.loads(req_reply.data().decode("utf-8"))
        return self._issue_from_data(data)
