"""Convert LLM markdown responses to natural spoken text.

Strips code blocks, markdown formatting, file paths, emojis, URLs, and other
visual artifacts that would sound unnatural when read aloud by a TTS engine.
"""

import re
from typing import Final

# ---------------------------------------------------------------------------
# Patterns
# ---------------------------------------------------------------------------

# Fenced code blocks (```lang\n...\n```)
_CODE_BLOCK_RE: Final[re.Pattern[str]] = re.compile(r"```[\w]*\n.*?```", re.DOTALL)

# Inline code (`...`)
_INLINE_CODE_RE: Final[re.Pattern[str]] = re.compile(r"`([^`]+)`")

# Markdown headings (# ... )
_HEADING_RE: Final[re.Pattern[str]] = re.compile(r"^#{1,6}\s+", re.MULTILINE)

# Bold/italic markers
_BOLD_ITALIC_RE: Final[re.Pattern[str]] = re.compile(r"(\*{1,3}|_{1,3})(.*?)\1")

# Markdown links [text](url)
_LINK_RE: Final[re.Pattern[str]] = re.compile(r"\[([^\]]+)\]\([^\)]+\)")

# Markdown images ![alt](url)
_IMAGE_RE: Final[re.Pattern[str]] = re.compile(r"!\[([^\]]*)\]\([^\)]+\)")

# Bare URLs (https://... or http://...)
_BARE_URL_RE: Final[re.Pattern[str]] = re.compile(r"https?://\S+")

# Bullet points (- or * at line start)
_BULLET_RE: Final[re.Pattern[str]] = re.compile(r"^[\s]*[-*+]\s+", re.MULTILINE)

# Numbered lists (1. at line start)
_NUMBERED_LIST_RE: Final[re.Pattern[str]] = re.compile(r"^[\s]*\d+\.\s+", re.MULTILINE)

# Horizontal rules (---, ***, ___)
_HR_RE: Final[re.Pattern[str]] = re.compile(r"^[-*_]{3,}\s*$", re.MULTILINE)

# HTML tags
_HTML_RE: Final[re.Pattern[str]] = re.compile(r"<[^>]+>")

# File paths (/foo/bar/baz.py or ./foo/bar)
_FILE_PATH_RE: Final[re.Pattern[str]] = re.compile(r"(?<!\w)(?:\.{0,2}/[\w./-]{4,})", re.MULTILINE)

# Emoji: Unicode emoji ranges (most common blocks)
_EMOJI_RE: Final[re.Pattern[str]] = re.compile(
    "["
    "\U0001f300-\U0001f9ff"  # Misc Symbols, Emoticons, Dingbats, Supplemental
    "\U00002600-\U000027bf"  # Misc Symbols, Dingbats
    "\U0000fe00-\U0000fe0f"  # Variation Selectors
    "\U0000200d"  # Zero Width Joiner
    "\U000020e3"  # Combining Enclosing Keycap
    "\U0001fa00-\U0001faff"  # Symbols & Pictographs Extended-A
    "\U00002702-\U000027b0"  # Dingbats
    "]+",
    re.UNICODE,
)

# Unicode arrows, bullets, and decorative characters
_SPECIAL_CHARS_RE: Final[re.Pattern[str]] = re.compile(r"[→←↑↓⇒⇐⇑⇓•◦▪▸▹►▻★☆✓✗✘✔✕❌⚠️]")

# Multiple newlines → single
_MULTI_NEWLINE_RE: Final[re.Pattern[str]] = re.compile(r"\n{3,}")

# Multiple spaces → single
_MULTI_SPACE_RE: Final[re.Pattern[str]] = re.compile(r" {2,}")

# Blockquotes (> at line start)
_BLOCKQUOTE_RE: Final[re.Pattern[str]] = re.compile(r"^>\s?", re.MULTILINE)

# Table rows (| ... | ... |)
_TABLE_ROW_RE: Final[re.Pattern[str]] = re.compile(r"^\|.*\|$", re.MULTILINE)

# Table separator rows (|---|---|)
_TABLE_SEP_RE: Final[re.Pattern[str]] = re.compile(r"^\|[-:\s|]+\|$", re.MULTILINE)


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def strip_for_speech(text: str) -> str:
    """Convert markdown-rich LLM output to natural spoken text.

    Removes code blocks, formatting markers, file paths, emojis, URLs,
    tables, and other visual elements.  Preserves the conversational meaning
    while producing text that sounds natural when spoken by a TTS engine.

    Args:
        text: Raw LLM response text (may contain markdown).

    Returns:
        Cleaned text suitable for speech synthesis.
    """
    if not text:
        return ""

    result = text

    # Remove code blocks first (replace with spoken summary)
    result = _CODE_BLOCK_RE.sub(" (code omitted) ", result)

    # Remove images before links (images are a superset of link syntax)
    result = _IMAGE_RE.sub(r"\1", result)

    # Replace links with just the link text
    result = _LINK_RE.sub(r"\1", result)

    # Remove bare URLs
    result = _BARE_URL_RE.sub("", result)

    # Remove table separator rows entirely
    result = _TABLE_SEP_RE.sub("", result)

    # Simplify table rows — strip pipes
    result = _TABLE_ROW_RE.sub(lambda m: m.group(0).replace("|", " ").strip(), result)

    # Remove HTML tags
    result = _HTML_RE.sub("", result)

    # Remove horizontal rules
    result = _HR_RE.sub("", result)

    # Remove heading markers (keep the text)
    result = _HEADING_RE.sub("", result)

    # Remove blockquote markers (keep the text)
    result = _BLOCKQUOTE_RE.sub("", result)

    # Remove bold/italic markers (keep the text)
    result = _BOLD_ITALIC_RE.sub(r"\2", result)

    # Simplify inline code — keep the text, drop backticks
    result = _INLINE_CODE_RE.sub(r"\1", result)

    # Remove bullet markers
    result = _BULLET_RE.sub("", result)

    # Remove numbered list markers
    result = _NUMBERED_LIST_RE.sub("", result)

    # Remove file paths (replace with just the filename)
    result = _FILE_PATH_RE.sub(_file_path_to_name, result)

    # Remove emojis
    result = _EMOJI_RE.sub("", result)

    # Remove special Unicode characters (arrows, bullets, etc.)
    result = _SPECIAL_CHARS_RE.sub("", result)

    # Clean up whitespace
    result = _MULTI_NEWLINE_RE.sub("\n\n", result)
    result = _MULTI_SPACE_RE.sub(" ", result)
    result = result.strip()

    return result


def _file_path_to_name(match: re.Match[str]) -> str:
    """Extract just the filename from a file path match."""
    path = match.group(0).strip()
    # Get the last component (filename)
    parts = path.rstrip("/").rsplit("/", 1)
    if len(parts) > 1 and parts[1]:
        return " " + parts[1]
    return ""
