import re
from typing import TYPE_CHECKING, Match, Optional, Pattern

from ..helpers import PREVENT_BACKSLASH

if TYPE_CHECKING:
    from ..core import BaseRenderer, InlineState
    from ..inline_parser import InlineParser
    from ..markdown import Markdown

__all__ = ["strikethrough", "mark", "insert", "superscript", "subscript"]

_STRIKE_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\~|[^\s~])~~(?!~)")
_MARK_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\=|[^\s=])==(?!=)")
_INSERT_END = re.compile(r"(?:" + PREVENT_BACKSLASH + r"\\\^|[^\s^])\^\^(?!\^)")

SUPERSCRIPT_PATTERN = r"\^(?:" + PREVENT_BACKSLASH + r"\\\^|\S|\\ )+?\^"
SUBSCRIPT_PATTERN = r"~(?:" + PREVENT_BACKSLASH + r"\\~|\S|\\ )+?~"


def parse_strikethrough(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
    return _parse_to_end(inline, m, state, "strikethrough", _STRIKE_END)


def render_strikethrough(renderer: "BaseRenderer", text: str) -> str:
    return "<del>" + text + "</del>"


def parse_mark(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
    return _parse_to_end(inline, m, state, "mark", _MARK_END)


def render_mark(renderer: "BaseRenderer", text: str) -> str:
    return "<mark>" + text + "</mark>"


def parse_insert(inline: "InlineParser", m: Match[str], state: "InlineState") -> Optional[int]:
    return _parse_to_end(inline, m, state, "insert", _INSERT_END)


def render_insert(renderer: "BaseRenderer", text: str) -> str:
    return "<ins>" + text + "</ins>"


def parse_superscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
    return _parse_script(inline, m, state, "superscript")


def render_superscript(renderer: "BaseRenderer", text: str) -> str:
    return "<sup>" + text + "</sup>"


def parse_subscript(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
    return _parse_script(inline, m, state, "subscript")


def render_subscript(renderer: "BaseRenderer", text: str) -> str:
    return "<sub>" + text + "</sub>"


def _parse_to_end(
    inline: "InlineParser",
    m: Match[str],
    state: "InlineState",
    tok_type: str,
    end_pattern: Pattern[str],
) -> Optional[int]:
    pos = m.end()
    m1 = end_pattern.search(state.src, pos)
    if not m1:
        return None
    end_pos = m1.end()
    text = state.src[pos : end_pos - 2]
    new_state = state.copy()
    new_state.src = text
    children = inline.render(new_state)
    state.append_token({"type": tok_type, "children": children})
    return end_pos


def _parse_script(inline: "InlineParser", m: Match[str], state: "InlineState", tok_type: str) -> int:
    text = m.group(0)
    new_state = state.copy()
    new_state.src = text[1:-1].replace("\\ ", " ")
    children = inline.render(new_state)
    state.append_token({"type": tok_type, "children": children})
    return m.end()


def strikethrough(md: "Markdown") -> None:
    """A mistune plugin to support strikethrough. Spec defined by
    GitHub flavored Markdown and commonly used by many parsers:

    .. code-block:: text

        ~~This was mistaken text~~

    It will be converted into HTML:

    .. code-block:: html

        <del>This was mistaken text</del>

    :param md: Markdown instance
    """
    md.inline.register(
        "strikethrough",
        r"~~(?=[^\s~])",
        parse_strikethrough,
        before="link",
    )
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("strikethrough", render_strikethrough)


def mark(md: "Markdown") -> None:
    """A mistune plugin to add ``<mark>`` tag. Spec defined at
    https://facelessuser.github.io/pymdown-extensions/extensions/mark/:

    .. code-block:: text

        ==mark me== ==mark \\=\\= equal==

    :param md: Markdown instance
    """
    md.inline.register(
        "mark",
        r"==(?=[^\s=])",
        parse_mark,
        before="link",
    )
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("mark", render_mark)


def insert(md: "Markdown") -> None:
    """A mistune plugin to add ``<ins>`` tag. Spec defined at
    https://facelessuser.github.io/pymdown-extensions/extensions/caret/#insert:

    .. code-block:: text

        ^^insert me^^

    :param md: Markdown instance
    """
    md.inline.register(
        "insert",
        r"\^\^(?=[^\s\^])",
        parse_insert,
        before="link",
    )
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("insert", render_insert)


def superscript(md: "Markdown") -> None:
    """A mistune plugin to add ``<sup>`` tag. Spec defined at
    https://pandoc.org/MANUAL.html#superscripts-and-subscripts:

    .. code-block:: text

        2^10^ is 1024.

    :param md: Markdown instance
    """
    md.inline.register("superscript", SUPERSCRIPT_PATTERN, parse_superscript, before="linebreak")
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("superscript", render_superscript)


def subscript(md: "Markdown") -> None:
    """A mistune plugin to add ``<sub>`` tag. Spec defined at
    https://pandoc.org/MANUAL.html#superscripts-and-subscripts:

    .. code-block:: text

        H~2~O is a liquid.

    :param md: Markdown instance
    """
    md.inline.register("subscript", SUBSCRIPT_PATTERN, parse_subscript, before="linebreak")
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("subscript", render_subscript)
