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)
