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

from ..util import escape as escape_text
from ..util import escape_url
from ._base import BaseDirective, DirectivePlugin

if TYPE_CHECKING:
    from ..block_parser import BlockParser
    from ..core import BlockState
    from ..markdown import Markdown
    from ..renderers.html import HTMLRenderer

__all__ = ['Image', 'Figure']

_num_re = re.compile(r'^\d+(?:\.\d*)?')
_allowed_aligns = ["top", "middle", "bottom", "left", "center", "right"]


def _parse_attrs(options: Dict[str, Any]) -> Dict[str, Any]:
    attrs = {}
    if 'alt' in options:
        attrs['alt'] = options['alt']

    # validate align
    align = options.get('align')
    if align and align in _allowed_aligns:
        attrs['align'] = align

    height = options.get('height')
    width = options.get('width')
    if height and _num_re.match(height):
        attrs['height'] = height
    if width and _num_re.match(width):
        attrs['width'] = width
    if 'target' in options:
        attrs['target'] = escape_url(options['target'])
    return attrs


class Image(DirectivePlugin):
    NAME = 'image'

    def parse(
        self, block: "BlockParser", m: Match[str], state: "BlockState"
    ) -> Dict[str, Any]:
        options = dict(self.parse_options(m))
        attrs = _parse_attrs(options)
        attrs['src'] = self.parse_title(m)
        return {'type': 'block_image', 'attrs': attrs}

    def __call__(self, directive: "BaseDirective", md: "Markdown") -> None:
        directive.register(self.NAME, self.parse)
        assert md.renderer is not None
        if md.renderer.NAME == 'html':
            md.renderer.register('block_image', render_block_image)


def render_block_image(
    self: 'HTMLRenderer',
    src: str,
    alt: Optional[str] = None,
    width: Optional[str] = None,
    height: Optional[str] = None,
    **attrs: Any,
) -> str:
    img = '<img src="' + escape_text(src) + '"'
    style = ''
    if alt:
        img += ' alt="' + escape_text(alt) + '"'
    if width:
        if width.isdigit():
            img += ' width="' + width + '"'
        else:
            style += 'width:' + width + ';'
    if height:
        if height.isdigit():
            img += ' height="' + height + '"'
        else:
            style += 'height:' + height + ';'
    if style:
        img += ' style="' + escape_text(style) + '"'

    img += ' />'

    _cls = 'block-image'
    align = attrs.get('align')
    if align:
        _cls += ' align-' + align

    target = attrs.get('target')
    if target:
        href = self.safe_url(target)
        outer = '<a class="' + _cls + '" href="' + href + '">'
        return outer + img + '</a>\n'
    else:
        return '<div class="' + _cls + '">' + img + '</div>\n'


class Figure(DirectivePlugin):
    NAME = 'figure'

    def parse_directive_content(
        self, block: "BlockParser", m: Match[str], state: "BlockState"
    ) -> Optional[List[Dict[str, Any]]]:
        content = self.parse_content(m)
        if not content:
            return None

        tokens = list(self.parse_tokens(block, content, state))
        caption = tokens[0]
        if caption['type'] == 'paragraph':
            caption['type'] = 'figcaption'
            children = [caption]
            if len(tokens) > 1:
                children.append({
                    'type': 'legend',
                    'children': tokens[1:]
                })
            return children
        return None

    def parse(
        self, block: "BlockParser", m: Match[str], state: "BlockState"
    ) -> Dict[str, Any]:
        options = dict(self.parse_options(m))
        image_attrs = _parse_attrs(options)
        image_attrs['src'] = self.parse_title(m)

        align = image_attrs.pop('align', None)
        fig_attrs = {}
        if align:
            fig_attrs['align'] = align
        for k in ['figwidth', 'figclass']:
            if k in options:
                fig_attrs[k] = options[k]

        children = [{'type': 'block_image', 'attrs': image_attrs}]
        content = self.parse_directive_content(block, m, state)
        if content:
            children.extend(content)
        return {
            'type': 'figure',
            'attrs': fig_attrs,
            'children': children,
        }

    def __call__(self, directive: "BaseDirective", md: "Markdown") -> None:
        directive.register(self.NAME, self.parse)

        assert md.renderer is not None
        if md.renderer.NAME == 'html':
            md.renderer.register('figure', render_figure)
            md.renderer.register('block_image', render_block_image)
            md.renderer.register('figcaption', render_figcaption)
            md.renderer.register('legend', render_legend)


def render_figure(
    self: Any,
    text: str,
    align: Optional[str] = None,
    figwidth: Optional[str] = None,
    figclass: Optional[str] = None,
) -> str:
    _cls = "figure"
    if align:
        _cls += ' align-' + align
    if figclass:
        _cls += ' ' + figclass

    html = '<figure class="' + _cls + '"'
    if figwidth:
        html += ' style="width:' + figwidth + '"'
    return html + '>\n' + text + '</figure>\n'


def render_figcaption(self: Any, text: str) -> str:
    return '<figcaption>' + text + '</figcaption>\n'


def render_legend(self: Any, text: str) -> str:
    return '<div class="legend">\n' + text + '</div>\n'
