import re

_SPECIAL_CHARS = {
    "&": r"\&",
    "%": r"\%",
    "$": r"\$",
    "#": r"\#",
    "_": r"\_",
    "{": r"\{",
    "}": r"\}",
    "~": r"\textasciitilde{}",
    "^": r"\textasciicircum{}",
    "\\": r"\textbackslash{}",
}
_SPECIAL_CHARS_RE = re.compile("|".join(re.escape(c) for c in _SPECIAL_CHARS))


def escape_latex(value: str) -> str:
    """Escape LaTeX special characters in plain user-entered text."""
    return _SPECIAL_CHARS_RE.sub(lambda m: _SPECIAL_CHARS[m.group(0)], value)


_STEP_BOLD_RE = re.compile(r"^\*\*(.+?)\*\*(.*)$")


def format_multiline(value: str) -> str:
    """Escape text and convert user newlines into LaTeX line breaks.

    Supports a leading "**Label**: rest" line convention (seen in the existing
    hand-written recipes) which becomes \\textbf{Label}: rest.
    """
    lines = value.strip().splitlines()
    rendered_lines = []
    for line in lines:
        match = _STEP_BOLD_RE.match(line)
        if match:
            label, rest = match.groups()
            rendered_lines.append(f"\\textbf{{{escape_latex(label)}}}{escape_latex(rest)}")
        else:
            rendered_lines.append(escape_latex(line))
    return " \\\\\n    ".join(rendered_lines)
