# retoor <retoor@molodetz.nl>

from __future__ import annotations

import html
from html.parser import HTMLParser

import mistune

MAX_MESSAGE_CHARS = 4096
CHUNK_BUDGET = 3900

_markdown = mistune.create_markdown(
    escape=False,
    hard_wrap=True,
    plugins=["table", "strikethrough", "url"],
)

INLINE_TAGS = {
    "strong": "b",
    "b": "b",
    "em": "i",
    "i": "i",
    "del": "s",
    "s": "s",
    "strike": "s",
    "u": "u",
    "ins": "u",
    "code": "code",
    "pre": "pre",
    "blockquote": "blockquote",
}

BLOCK_TAGS = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "table", "tr"}
HEADING_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6"}


class _TelegramHtml(HTMLParser):
    def __init__(self) -> None:
        super().__init__(convert_charrefs=True)
        self._out: list[str] = []
        self._list_stack: list[dict] = []
        self._in_heading = False
        self._in_pre = False

    def handle_starttag(self, tag: str, attrs: list) -> None:
        mapping = dict(attrs)
        if tag == "br":
            self._out.append("\n")
            return
        if tag == "hr":
            self._out.append("\n")
            return
        if tag in HEADING_TAGS:
            self._newline_break()
            self._out.append("<b>")
            self._in_heading = True
            return
        if tag in ("ul", "ol"):
            self._newline_break()
            self._list_stack.append({"ordered": tag == "ol", "n": 0})
            return
        if tag == "li":
            self._out.append("\n")
            if self._list_stack:
                top = self._list_stack[-1]
                if top["ordered"]:
                    top["n"] += 1
                    self._out.append(f"{top['n']}. ")
                else:
                    self._out.append("- ")
            return
        if tag in BLOCK_TAGS:
            self._newline_break()
            return
        if tag == "a":
            href = mapping.get("href", "")
            if href.startswith(("http://", "https://", "tg://")):
                self._out.append(f'<a href="{html.escape(href, quote=True)}">')
            return
        if tag == "pre":
            self._newline_break()
            self._out.append("<pre>")
            self._in_pre = True
            return
        target = INLINE_TAGS.get(tag)
        if target:
            self._out.append(f"<{target}>")

    def handle_endtag(self, tag: str) -> None:
        if tag in HEADING_TAGS:
            self._out.append("</b>\n")
            self._in_heading = False
            return
        if tag in ("ul", "ol"):
            if self._list_stack:
                self._list_stack.pop()
            self._out.append("\n")
            return
        if tag in BLOCK_TAGS or tag == "li":
            return
        if tag == "a":
            if self._out and "<a href=" in "".join(self._out[-6:]):
                self._out.append("</a>")
            return
        if tag == "pre":
            self._out.append("</pre>\n")
            self._in_pre = False
            return
        target = INLINE_TAGS.get(tag)
        if target:
            self._out.append(f"</{target}>")

    def handle_data(self, data: str) -> None:
        self._out.append(html.escape(data, quote=False))

    def _newline_break(self) -> None:
        text = "".join(self._out)
        if text and not text.endswith("\n\n"):
            if text.endswith("\n"):
                self._out.append("\n")
            else:
                self._out.append("\n\n")

    def result(self) -> str:
        text = "".join(self._out)
        while "\n\n\n" in text:
            text = text.replace("\n\n\n", "\n\n")
        return text.strip()


def markdown_to_telegram_html(text: str) -> str:
    rendered = _markdown(text or "")
    parser = _TelegramHtml()
    parser.feed(rendered)
    parser.close()
    return parser.result()


def split_for_telegram(text: str, limit: int = CHUNK_BUDGET) -> list[str]:
    if len(text) <= limit:
        return [text] if text else [""]
    chunks: list[str] = []
    current = ""
    for line in text.split("\n"):
        piece = line if not current else "\n" + line
        if len(current) + len(piece) <= limit:
            current += piece
            continue
        if current:
            chunks.append(current)
            current = ""
        if len(line) <= limit:
            current = line
            continue
        for start in range(0, len(line), limit):
            segment = line[start : start + limit]
            if len(segment) == limit:
                chunks.append(segment)
            else:
                current = segment
    if current:
        chunks.append(current)
    return chunks or [""]
