#  standard
import json
import re

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

# plugin
from oslandia.__about__ import __version__
from oslandia.constants import UTM_CONTENT_ISSUEBROWSER
from oslandia.toolbelt import PlgLogger, PlgOptionsManager
from oslandia.toolbelt.network_manager import NetworkRequestsManager


# -- Globals --
REGEX_PATTERN_MD_HTML_COMMENTS: re.Pattern[str] = re.compile(
    r"<!--.*?-->", flags=re.DOTALL
)
REGEX_PATTERN_MD_HTML_COMMENT_ONLY_LINES: re.Pattern[str] = re.compile(
    r"^[ \t]*<!--.*?-->[ \t]*\n?",
    flags=re.DOTALL | re.MULTILINE,
)
WATERMARK_PREFIX = "<!-- src:qgis-"
WATERMARK_PLUGIN_MARKER = "-plugin-"
WATERMARK_SUFFIX = " -->"


# -- Functions --
def get_watermark(
    version: str | None = None, qgis_version: int | str | None = None
) -> str:
    """Build the markdown watermark comment.

    :param version: plugin version to embed in the watermark, defaults to the
        package version.
    :type version: str | None
    :param qgis_version: QGIS version to embed in the watermark, defaults to the
        current QGIS version.
    :type qgis_version: int | str | None

    :return: HTML comment watermark including the plugin version.
    :rtype: str
    """
    plugin_version = (version or __version__).strip()
    current_qgis_version = str(qgis_version or Qgis.QGIS_VERSION_INT).strip()
    return f"<!-- src:qgis-{current_qgis_version}-plugin-{plugin_version} -->"


def has_watermark(text: str | None) -> bool:
    """Check whether a markdown text already contains a watermark.

    :param text: markdown text to inspect.
    :type text: str | None

    :return: True when a plugin watermark is already present, False otherwise.
    :rtype: bool
    """
    if not text:
        return False

    start = text.find(WATERMARK_PREFIX)
    if start == -1:
        return False

    marker_start = text.find(WATERMARK_PLUGIN_MARKER, start + len(WATERMARK_PREFIX))
    if marker_start == -1:
        return False

    return (
        text.find(WATERMARK_SUFFIX, marker_start + len(WATERMARK_PLUGIN_MARKER)) != -1
    )


def append_watermark(
    text: str | None,
    version: str | None = None,
    qgis_version: int | str | None = None,
) -> str:
    """Append the watermark at the end of markdown content.

    The watermark is added once and separated from the content by a blank line
    when the input is not empty.

    :param text: markdown content to decorate.
    :type text: str | None
    :param version: plugin version to embed in the watermark, defaults to the
        package version.
    :type version: str | None
    :param qgis_version: QGIS version to embed in the watermark, defaults to the
        current QGIS version.
    :type qgis_version: int | str | None

    :return: markdown content with a single plugin watermark appended.
    :rtype: str
    """
    content = text or ""
    if has_watermark(content):
        return content

    watermark = get_watermark(version, qgis_version)
    if not content:
        return watermark
    if content.endswith("\n\n"):
        return f"{content}{watermark}"
    if content.endswith("\n"):
        return f"{content}\n{watermark}"
    return f"{content}\n\n{watermark}"


def strip_markdown_html_comments(text: str | None) -> str | None:
    """Remove HTML-style comments from markdown content.

    :param text: markdown text to sanitize.
    :type text: str | None

    :return: markdown text without HTML-style comments.
    :rtype: str | None
    """
    if text is None:
        return None

    text_without_comment_only_lines = REGEX_PATTERN_MD_HTML_COMMENT_ONLY_LINES.sub(
        "",
        text,
    )

    return REGEX_PATTERN_MD_HTML_COMMENTS.sub("", text_without_comment_only_lines)


# -- Classes --
class MarkdownRequestManager:
    def __init__(self):
        """Manager to use Gitlab API for markdown render"""
        self.log = PlgLogger().log
        self.ntwk_requester_blk = NetworkRequestsManager()
        self.plg_settings = PlgOptionsManager.get_plg_settings()

    def convert_markdown_to_html(
        self, text: str, project_path: str | None
    ) -> str | None:
        """Convert markdown to html with Gitlab API

        :param text: markdown text
        :type text: str
        :param project_path: project path. Needed to get redirection with gitlab format markdown
        :type project_path: Optional[str]
        :return: markdown converted to html. None if no reply from Gitlab API
        :rtype: Optional[str]
        """
        # encode data
        data = QByteArray()
        data_map = {
            "text": text,
            "gfm": self.plg_settings.use_gitlab_markdown_format,
        }
        if project_path:
            data_map["project"] = project_path
        data.append(json.dumps(data_map).encode("utf-8"))

        # send request
        req_reply = self.ntwk_requester_blk.post_url(
            url=QUrl(f"{self.plg_settings.gitlab_api_url_base}markdown"),
            config_id=self.plg_settings.authentification_config_id,
            data=data,
            content_type_header="application/json",
            caller=UTM_CONTENT_ISSUEBROWSER,
        )
        if req_reply:
            data = json.loads(req_reply.data().decode("utf-8"))
            return data["html"]
        else:
            return None

    @staticmethod
    def fix_gitlab_flavor_lazy_images(html: str) -> str:
        """Fix gitlab flavor html with lazy images load with src and data-src.
        Needed to use html with QTextBrowser other src will be asked and no image will be displayed

        :param html: html with lazy image
        :type html: str
        :return: converted html with data-src replaced by src
        :rtype: str
        """
        # Replace data-src="URL" with src="URL" for lazy images
        # Regexp generated with claude.ai
        html = re.sub(
            r'(<img[^>]*?)src="data:image/gif;base64,[^"]*"([^>]*?)data-src="([^"]*)"',
            r'\1src="\3"\2',
            html,
        )
        return html
