import re
from typing import TYPE_CHECKING, Any, Dict, List, Match, Optional

from ..helpers import parse_link, parse_link_label
from ..util import unikey

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


RUBY_PATTERN = r"\[(?:\w+\([\w ]+\))+\]"
_ruby_re = re.compile(RUBY_PATTERN)


def parse_ruby(inline: "InlineParser", m: Match[str], state: "InlineState") -> int:
    while True:
        tokens = _parse_ruby_tokens(m)
        end_pos = m.end()
        next_match = _ruby_re.match(state.src, end_pos)
        if not next_match:
            break
        for tok in tokens:
            state.append_token(tok)
        m = next_match

    # repeat link logic
    if end_pos < len(state.src):
        link_pos = _parse_ruby_link(inline, state, end_pos, tokens)
        if link_pos:
            return link_pos

    for tok in tokens:
        state.append_token(tok)
    return end_pos


def _parse_ruby_tokens(m: Match[str]) -> List[Dict[str, Any]]:
    text = m.group(0)[1:-2]
    tokens = []
    for item in text.split(")"):
        rb, rt = item.split("(")
        tokens.append({"type": "ruby", "raw": rb, "attrs": {"rt": rt}})
    return tokens


def _parse_ruby_link(
    inline: "InlineParser", state: "InlineState", pos: int, tokens: List[Dict[str, Any]]
) -> Optional[int]:
    c = state.src[pos]
    if c == "(":
        # standard link [text](<url> "title")
        attrs, link_pos = parse_link(state.src, pos + 1)
        if link_pos:
            state.append_token(
                {
                    "type": "link",
                    "children": tokens,
                    "attrs": attrs,
                }
            )
            return link_pos

    elif c == "[":
        # standard ref link [text][label]
        label, link_pos = parse_link_label(state.src, pos + 1)
        if label and link_pos:
            ref_links = state.env["ref_links"]
            key = unikey(label)
            env = ref_links.get(key)
            if env:
                attrs = {"url": env["url"], "title": env.get("title")}
                state.append_token(
                    {
                        "type": "link",
                        "children": tokens,
                        "attrs": attrs,
                    }
                )
            else:
                for tok in tokens:
                    state.append_token(tok)
                state.append_token(
                    {
                        "type": "text",
                        "raw": "[" + label + "]",
                    }
                )
            return link_pos
    return None


def render_ruby(renderer: "BaseRenderer", text: str, rt: str) -> str:
    return "<ruby>" + text + "<rt>" + rt + "</rt></ruby>"


def ruby(md: "Markdown") -> None:
    """A mistune plugin to support ``<ruby>`` tag. The syntax is defined
    at https://lepture.com/en/2022/markdown-ruby-markup:

    .. code-block:: text

        [漢字(ㄏㄢˋㄗˋ)]
        [漢(ㄏㄢˋ)字(ㄗˋ)]

        [漢字(ㄏㄢˋㄗˋ)][link]
        [漢字(ㄏㄢˋㄗˋ)](/url "title")

        [link]: /url "title"

    :param md: Markdown instance
    """
    md.inline.register("ruby", RUBY_PATTERN, parse_ruby, before="link")
    if md.renderer and md.renderer.NAME == "html":
        md.renderer.register("ruby", render_ruby)
