from __future__ import annotations

import html
import re
from typing import Optional

from ..chat.text_sanitization import prepare_outbound_source_text
from .constants import TELEGRAM_MAX_MESSAGE_LENGTH

_CODE_BLOCK_RE = re.compile(r"```(?:[^\n`]*)\n(.*?)```", re.DOTALL)
_INLINE_CODE_RE = re.compile(r"`([^`\n]+)`")
_BOLD_RE = re.compile(r"\*\*(.+?)\*\*")
_MARKDOWN_ESCAPE_RE = re.compile(r"([_*\[\]\(\)`])")
_MARKDOWN_V2_ESCAPE_RE = re.compile(r"([_*\[\]\(\)~`>#+\-=|{}.!\\])")


def sanitize_telegram_outbound_text(text: str) -> str:
    if not text:
        return ""
    return prepare_outbound_source_text(text)


def _format_telegram_html(text: str) -> str:
    if not text:
        return ""
    text = sanitize_telegram_outbound_text(text)
    return _format_telegram_html_sanitized(text)


def _format_telegram_html_sanitized(text: str) -> str:
    if not text:
        return ""
    parts: list[str] = []
    last = 0
    for match in _CODE_BLOCK_RE.finditer(text):
        parts.append(_format_telegram_inline(text[last : match.start()]))
        code = match.group(1)
        parts.append("<pre><code>")
        parts.append(html.escape(code, quote=False))
        parts.append("</code></pre>")
        last = match.end()
    parts.append(_format_telegram_inline(text[last:]))
    return "".join(parts)


def _format_telegram_inline(text: str) -> str:
    if not text:
        return ""
    placeholders: list[str] = []

    def _replace_code(match: re.Match[str]) -> str:
        placeholders.append(html.escape(match.group(1), quote=False))
        return f"\x00CODE{len(placeholders) - 1}\x00"

    text = _INLINE_CODE_RE.sub(_replace_code, text)
    escaped = html.escape(text, quote=False)
    escaped = _BOLD_RE.sub(lambda match: f"<b>{match.group(1)}</b>", escaped)
    for idx, code in enumerate(placeholders):
        token = f"\x00CODE{idx}\x00"
        escaped = escaped.replace(token, f"<code>{code}</code>")
    return escaped


def _escape_markdown_text(text: str, *, version: str) -> str:
    if not text:
        return ""
    if version == "MarkdownV2":
        return _MARKDOWN_V2_ESCAPE_RE.sub(r"\\\1", text)
    return _MARKDOWN_ESCAPE_RE.sub(r"\\\1", text)


def _escape_markdown_code(text: str, *, version: str) -> str:
    if not text:
        return ""
    if version == "MarkdownV2":
        return text.replace("\\", "\\\\").replace("`", "\\`")
    return text.replace("`", "\\`")


def _format_telegram_markdown(text: str, version: str) -> str:
    if not text:
        return ""
    text = sanitize_telegram_outbound_text(text)
    return _format_telegram_markdown_sanitized(text, version)


def _format_telegram_markdown_sanitized(text: str, version: str) -> str:
    if not text:
        return ""
    parts: list[str] = []
    last = 0
    for match in _CODE_BLOCK_RE.finditer(text):
        parts.append(
            _format_telegram_markdown_inline(text[last : match.start()], version)
        )
        code = _escape_markdown_code(match.group(1), version=version)
        parts.append(f"```\n{code}\n```")
        last = match.end()
    parts.append(_format_telegram_markdown_inline(text[last:], version))
    return "".join(parts)


def _format_telegram_markdown_inline(text: str, version: str) -> str:
    if not text:
        return ""
    code_placeholders: list[str] = []
    bold_placeholders: list[str] = []

    def _replace_code(match: re.Match[str]) -> str:
        code_placeholders.append(_escape_markdown_code(match.group(1), version=version))
        return f"\x00CODE{len(code_placeholders) - 1}\x00"

    def _replace_bold(match: re.Match[str]) -> str:
        bold_placeholders.append(_escape_markdown_text(match.group(1), version=version))
        return f"\x00BOLD{len(bold_placeholders) - 1}\x00"

    text = _INLINE_CODE_RE.sub(_replace_code, text)
    text = _BOLD_RE.sub(_replace_bold, text)
    escaped = _escape_markdown_text(text, version=version)
    for idx, bold in enumerate(bold_placeholders):
        token = f"\x00BOLD{idx}\x00"
        escaped = escaped.replace(token, f"*{bold}*")
    for idx, code in enumerate(code_placeholders):
        token = f"\x00CODE{idx}\x00"
        escaped = escaped.replace(token, f"`{code}`")
    return escaped


def render_telegram_message(
    text: str, *, parse_mode: Optional[str] = None
) -> tuple[str, Optional[str]]:
    sanitized = sanitize_telegram_outbound_text(text)
    if not parse_mode:
        return sanitized, None
    if parse_mode == "HTML":
        return _format_telegram_html_sanitized(sanitized), parse_mode
    if parse_mode in ("Markdown", "MarkdownV2"):
        return _format_telegram_markdown_sanitized(sanitized, parse_mode), parse_mode
    return sanitized, parse_mode


def prepare_telegram_message(
    text: str,
    *,
    parse_mode: Optional[str] = None,
    max_length: int = TELEGRAM_MAX_MESSAGE_LENGTH,
) -> tuple[str, Optional[str]]:
    rendered, resolved_parse_mode = render_telegram_message(text, parse_mode=parse_mode)
    if resolved_parse_mode and len(rendered) <= max_length:
        return rendered, resolved_parse_mode
    return sanitize_telegram_outbound_text(text), None
