"""fala_pt.py — o texto vira o que se FALA, em português do Brasil, antes de ir para a voz.

Rede de segurança: o cérebro já devolve um `falar` escrito para ser ouvido (app.py), e isto limpa o resto —
markdown, links, caminhos, código, emojis — e diz por extenso números, unidades, dinheiro, horas, datas,
versões e siglas ("32 s" → "trinta e dois segundos", "R$ 99" → "noventa e nove reais", "PR" → "pê érre").
Puro: sem rede, sem Mac. Python 3.9+.
"""
from __future__ import annotations

import re
from typing import List

_UN = ["zero", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove", "dez", "onze", "doze", "treze",
       "catorze", "quinze", "dezesseis", "dezessete", "dezoito", "dezenove"]
_DZ = ["", "", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa"]
_CT = ["", "cento", "duzentos", "trezentos", "quatrocentos", "quinhentos", "seiscentos", "setecentos", "oitocentos", "novecentos"]


def _ate_999(n: int) -> str:
    if n < 20:
        return _UN[n]
    if n < 100:
        d, u = divmod(n, 10)
        return _DZ[d] + (" e " + _UN[u] if u else "")
    if n == 100:
        return "cem"
    c, r = divmod(n, 100)
    return _CT[c] + (" e " + _ate_999(r) if r else "")


def extenso(n: int, feminino: bool = False) -> str:
    """0..999.999.999.999 por extenso ("mil duzentos e trinta e quatro")."""
    if n < 0:
        return "menos " + extenso(-n, feminino)
    if n < 1000:
        t = _ate_999(n)
        if feminino:
            t = re.sub(r"\bum\b", "uma", t)
            t = re.sub(r"\bdois\b", "duas", t)
            t = re.sub(r"(\w+)entos\b", r"\1entas", t)
        return t
    partes: List[str] = []
    for valor, sing, plur in ((10 ** 9, "bilhão", "bilhões"), (10 ** 6, "milhão", "milhões"), (1000, "mil", "mil")):
        q, n = divmod(n, valor)
        if not q:
            continue
        if valor == 1000:
            partes.append("mil" if q == 1 else extenso(q, feminino) + " mil")
        else:
            partes.append(extenso(q) + " " + (sing if q == 1 else plur))
    if n:
        partes.append(("e " if n < 100 or n % 100 == 0 else "") + extenso(n, feminino))
    return " ".join(partes).replace("  ", " ")


def _inteiro(txt: str) -> int:
    return int(txt.replace(".", "")) if re.fullmatch(r"\d{1,3}(?:\.\d{3})+", txt) else int(txt)


def _decimal(parte_int: str, parte_dec: str) -> str:
    return f"{extenso(_inteiro(parte_int))} vírgula " + " ".join(extenso(int(d)) for d in parte_dec) if parte_dec.startswith("0") \
        else f"{extenso(_inteiro(parte_int))} vírgula {extenso(int(parte_dec))}"


def _num(txt: str) -> str:
    if "," in txt:
        i, d = txt.split(",", 1)
        return _decimal(i, d)
    return extenso(_inteiro(txt))


_UNIDADES = {
    "ms": ("milissegundo", "milissegundos"), "s": ("segundo", "segundos"), "seg": ("segundo", "segundos"),
    "min": ("minuto", "minutos"), "h": ("hora", "horas"), "px": ("pixel", "pixels"),
    "kb": ("kilobyte", "kilobytes"), "mb": ("megabyte", "megabytes"), "gb": ("gigabyte", "gigabytes"), "tb": ("terabyte", "terabytes"),
    "km": ("quilômetro", "quilômetros"), "kg": ("quilo", "quilos"), "g": ("grama", "gramas"), "cm": ("centímetro", "centímetros"),
    "mm": ("milímetro", "milímetros"), "m": ("metro", "metros"), "fps": ("quadro por segundo", "quadros por segundo"),
    "mhz": ("megahertz", "megahertz"), "ghz": ("gigahertz", "gigahertz"), "hz": ("hertz", "hertz"),
    "kbps": ("kilobit por segundo", "kilobits por segundo"), "mbps": ("megabit por segundo", "megabits por segundo"),
}
_SIGLAS = {
    "PR": "pê érre", "PRs": "pê érres", "npm": "ene pê eme", "CTA": "chamada", "API": "á pê í", "APIs": "á pê ís", "CSS": "cê ésse ésse",
    "HTML": "agá tê eme éle", "JSON": "jêi són", "URL": "u érre éle", "CLI": "cê éle í", "CPU": "cê pê u", "RAM": "ram", "UI": "u í",
    "UX": "u ékis", "SQL": "ésse quê éle", "SVG": "ésse vê gê", "PDF": "pê dê éfe", "MP3": "ême pê três", "MP4": "ême pê quatro",
    "IA": "í á", "GPU": "gê pê u", "SSD": "ésse ésse dê", "TTS": "tê tê ésse", "STT": "ésse tê tê", "VAD": "vê á dê", "OK": "ôquei",
    "TCC": "tê cê cê", "SDK": "ésse dê cá", "CI": "cê í", "CD": "cê dê", "ID": "í dê", "TI": "tê í", "QA": "quê á", "OKR": "ô cá érre",
    "git": "guit", "GitHub": "guit rrâb", "README": "rídmi", "Spotify": "Spotifái", "WhatsApp": "uótsápi", "macOS": "mák ó ésse",
}
_LETRAS = dict(zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "á bê cê dê é éfe gê agá í jota cá éle ême ene ó pê quê érre ésse tê u vê dáblio ékis ípsilon zê".split()))
_MESES = ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"]


def _dinheiro(m: "re.Match") -> str:
    ints, cents = m.group(1), m.group(2)
    n = _inteiro(ints)
    t = extenso(n) + (" real" if n == 1 else " reais")
    if cents and int(cents):
        c = int(cents)
        t += " e " + extenso(c) + (" centavo" if c == 1 else " centavos")
    return t


def _unidade(m: "re.Match") -> str:
    num, un = m.group(1), m.group(2)
    forma = _UNIDADES.get(un.lower())
    if not forma or (len(un) == 1 and un not in "smhg" and un != "m"):
        return m.group(0)
    singular = num in ("1", "01") or num == "1,0"
    return f"{_num(num)} {forma[0] if singular else forma[1]}"


def normalizar(texto: str) -> str:
    t = str(texto or "")
    t = re.sub(r"```[\s\S]*?```", " ", t)                                    # bloco de código
    t = re.sub(r"`([^`\n]*)`", lambda m: "" if re.search(r"[/\\.]|\(|\)", m.group(1)) else m.group(1), t)
    t = re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", t)                          # [texto](link) → texto
    t = re.sub(r"https?://\S+|www\.\S+", "", t)
    t = re.sub(r"(?<![\w])(?:~|\.{1,2})?/[\w.\-@%+/]+", " ", t)              # caminhos /a/b.js ~/Primo/x
    t = re.sub(r"\b[\w\-]+(?:/[\w.\-]+)+\.\w+\b", " ", t)                    # a/b/c.js
    t = re.sub(r"\b[\w\-]+\.(?:js|ts|tsx|jsx|py|html|css|json|md|sh|yml|yaml|png|jpg|svg|mp4|wav|mp3|pdf)\b", "um arquivo", t)
    t = re.sub(r"^\s*(?:#{1,6}|[-*•]|\d+[.)])\s+", "", t, flags=re.M)        # títulos e marcadores
    t = re.sub(r"[*_#>|~]+", " ", t)
    t = re.sub(r"[\U0001F000-\U0001FAFF☀-➿⬀-⯿️‍←-⇿⌀-⏿●▸▪▣⎿]", " ", t)  # emojis e símbolos
    # dinheiro
    t = re.sub(r"R\$\s*(\d{1,3}(?:\.\d{3})*|\d+)(?:,(\d{1,2}))?", _dinheiro, t)
    # versões: v10.15.2 / 10.15.2 / v10.15
    t = re.sub(r"\bv(\d+(?:\.\d+){1,3})\b", lambda m: "versão " + " ponto ".join(extenso(int(x)) for x in m.group(1).split(".")), t)
    t = re.sub(r"\b(\d+(?:\.\d+){2,3})\b", lambda m: " ponto ".join(extenso(int(x)) for x in m.group(1).split(".")), t)
    # hora e data
    t = re.sub(r"\b([01]?\d|2[0-3]):([0-5]\d)\b", lambda m: extenso(int(m.group(1))) + (" horas" if int(m.group(1)) != 1 else " hora") + (" e " + extenso(int(m.group(2))) if int(m.group(2)) else ""), t)
    t = re.sub(r"\b(0?[1-9]|[12]\d|3[01])/(0?[1-9]|1[0-2])(?:/(\d{4}))?\b",
               lambda m: extenso(int(m.group(1))) + " de " + _MESES[int(m.group(2)) - 1] + (" de " + extenso(int(m.group(3))) if m.group(3) else ""), t)
    # porcentagem
    t = re.sub(r"(\d+(?:,\d+)?)\s?%", lambda m: _num(m.group(1)) + " por cento", t)
    # número + unidade ("32 s", "10px", "1,5 GB")
    t = re.sub(r"(?<![\w.,])(\d{1,3}(?:\.\d{3})+|\d+(?:,\d+)?)\s?([A-Za-z]{1,4})\b(?!\w)", _unidade, t)
    # siglas e nomes técnicos (palavra inteira)
    for sig in sorted(_SIGLAS, key=len, reverse=True):
        t = re.sub(r"(?<![\w])" + re.escape(sig) + r"(?![\w])", _SIGLAS[sig], t)
    t = re.sub(r"(?<![\w])([A-Z]{2,4})(?![\w])", lambda m: " ".join(_LETRAS[c] for c in m.group(1)), t)   # sigla desconhecida: soletra
    # números que sobraram
    t = re.sub(r"(?<![\w.])(\d{1,3}(?:\.\d{3})+|\d+),(\d+)(?![\w])", lambda m: _decimal(m.group(1), m.group(2)), t)
    t = re.sub(r"(?<![\w.,])(\d{1,3}(?:\.\d{3})+|\d+)(?![\w.,]\d)", lambda m: extenso(_inteiro(m.group(1))), t)
    t = re.sub(r"\s*[()\[\]{}]\s*", ", ", t)
    t = re.sub(r"\s*[—–]\s*", ", ", t)
    t = re.sub(r"\s*[&]\s*", " e ", t)
    t = re.sub(r"\s*\+\s*", " mais ", t)
    t = re.sub(r"\s*=>?\s*", " ", t)
    t = re.sub(r"\s+([,.!?;:])", r"\1", t)
    t = re.sub(r"(,\s*){2,}", ", ", t)
    return re.sub(r"\s+", " ", t).strip(" ,")
