"""Gerador de placeholder numérico da lumo-mockup.

Todo número exibido num mockup é uma fatia da sequência 1234567890, formatada
no padrão brasileiro, e é SEMPRE O MESMO dentro de um widget. A regra e a
heurística de escala estão em references/forma-sem-magnitude.md §0 e §2.

    from placeholder import placeholder, escala_de
    placeholder(escala_de("faturamento mensal"))   -> 'R$ 123.456,78'
    placeholder("milhoes")                          -> 'R$ 1.234.567,89'
    placeholder("milhares", prefixo="", decimais=0) -> '1.234'
"""

DIGITOS = "1234567890"

# escala -> (dígitos inteiros, casas decimais). Ver §2a.
ESCALAS = {
    "centenas": (3, 2),
    "milhares": (4, 2),
    "dezenas-de-milhares": (5, 2),
    "centenas-de-milhares": (6, 2),
    "milhoes": (7, 2),
    "dezenas-de-milhoes": (8, 2),
    "centenas-de-milhoes": (9, 2),
}

ORDEM = list(ESCALAS)

# Defaults por tipo de métrica quando não há âncora declarada. Ver §2b.
DEFAULT_POR_METRICA = {
    "faturamento-mensal": "centenas-de-milhares",
    "receita-mensal": "centenas-de-milhares",
    "faturamento-anual": "milhoes",
    "custo-mensal": "dezenas-de-milhares",
    "despesa-mensal": "dezenas-de-milhares",
    "ticket-medio": "centenas",
    "preco-unitario": "centenas",
    "contagem-documento-mes": "milhares",
    "contagem-cliente": "centenas",
}

# Formato abreviado, o `numberFormat: reduced` do produto (K/M/B em pt-BR).
# A ficha pede isso no campo `encoding.formato` ("R$ mil", "R$ mi"), e é o que
# um KPI executivo usa: ninguém lê sete dígitos num card. Uma escala por linha,
# sem pular nenhuma de milhares para cima: escala sem entrada aqui vira erro em
# vez de cair num vizinho errado.
ABREVIADO = {
    "milhares": "R$ 1,2 mil",
    "dezenas-de-milhares": "R$ 12,3 mil",
    "centenas-de-milhares": "R$ 123,5 mil",
    "milhoes": "R$ 1,2 mi",
    "dezenas-de-milhoes": "R$ 12,3 mi",
    "centenas-de-milhoes": "R$ 123,5 mi",
}

# Tipos que não escolhem escala. Ver §2b.
PERCENTUAL = "12,3%"
PERCENTUAL_ACIMA_100 = "123,4%"
TEMPO = {"horas": "1h 23m", "minutos": "12m 34s", "dias": "1d 2h 34m"}


def _milhar(s: str) -> str:
    """Separador de milhar brasileiro."""
    out = ""
    n = len(s)
    for i, c in enumerate(s):
        out += c
        resto = n - i - 1
        if resto and resto % 3 == 0:
            out += "."
    return out


def placeholder(escala: str, prefixo: str = "R$ ", decimais: int | None = None) -> str:
    """A fatia de 1234567890 correspondente à escala, formatada em pt-BR."""
    if escala not in ESCALAS:
        raise ValueError(
            f"escala desconhecida: {escala!r}. Use uma de {ORDEM}, "
            "ou o slab quando não houver escala honesta (§2b)."
        )
    inteiros, dec_padrao = ESCALAS[escala]
    dec = dec_padrao if decimais is None else decimais
    seq = (DIGITOS * 4)[: inteiros + dec]
    s = _milhar(seq[:inteiros])
    if dec:
        s += "," + seq[inteiros:]
    return f"{prefixo}{s}"


def contagem(escala: str) -> str:
    """Contagem não tem símbolo nem decimal."""
    return placeholder(escala, prefixo="", decimais=0)


def abreviado(escala: str, prefixo: str = "R$ ") -> str:
    """Placeholder no formato abreviado (`R$ 123,5 mil`).

    Use quando `encoding.formato` da ficha pede abreviação ("R$ mil", "R$ mi"),
    que é o caso comum de KPI executivo e de eixo com valor grande. Mantém a
    mesma sequência 1234567890, só que cortada e com sufixo.
    """
    if escala not in ABREVIADO:
        raise ValueError(
            f"escala {escala!r} não tem forma abreviada. Abreviar só faz sentido "
            f"de milhares para cima; para {escala!r} use placeholder() e deixe o "
            f"valor cheio, que é o que o produto faria."
        )
    v = ABREVIADO[escala]
    return v if prefixo == "R$ " else v.replace("R$ ", prefixo)


def do_formato(formato: str, escala: str) -> str:
    """Traduz o campo `encoding.formato` da ficha no placeholder certo.

    'R$ mil' / 'R$ mi'  -> abreviado
    'R$'                -> monetário cheio
    '%'                 -> percentual
    'n/a'               -> string vazia (o widget não exibe número)
    """
    f = formato.strip().lower()
    if f in ("n/a", "", "-"):
        return ""
    if f.startswith("%") or f.endswith("%"):
        return PERCENTUAL
    if any(x in f for x in ("mil", "mi", "bi", "k", "m", "b")) and "r$" in f:
        return abreviado(escala)
    if "r$" in f:
        return placeholder(escala)
    return placeholder(escala)


def escala_de(metrica: str) -> str:
    """Default por tipo de métrica, quando não há âncora declarada."""
    chave = metrica.strip().lower().replace(" ", "-").replace("_", "-")
    if chave not in DEFAULT_POR_METRICA:
        raise ValueError(
            f"métrica sem default: {metrica!r}. Declare a âncora do cliente "
            "ou use o slab e registre a incógnita (§2b)."
        )
    return DEFAULT_POR_METRICA[chave]


def acima(escala: str, passos: int = 1) -> str:
    """Uma escala acima. Usado pela linha Total e pelo nível pai da Matrix (§2c)."""
    i = min(ORDEM.index(escala) + passos, len(ORDEM) - 1)
    return ORDEM[i]


def abaixo(escala: str, passos: int = 1) -> str:
    i = max(ORDEM.index(escala) - passos, 0)
    return ORDEM[i]


TODOS = (
    [placeholder(e) for e in ORDEM]
    + [contagem(e) for e in ORDEM]
    + list(ABREVIADO.values())
    + [PERCENTUAL, PERCENTUAL_ACIMA_100]
    + list(TEMPO.values())
)
"""Todo placeholder válido. O assert anti-vazamento compara contra esta lista."""


if __name__ == "__main__":
    print("monetário:")
    for e in ORDEM:
        print(f"  {e:<24} {placeholder(e)}")
    print("\ncontagem:")
    for e in ORDEM[:4]:
        print(f"  {e:<24} {contagem(e)}")
    print(f"\npercentual: {PERCENTUAL}   acima de 100%: {PERCENTUAL_ACIMA_100}")
    print("tempo:", ", ".join(TEMPO.values()))
    print("\nabreviado (encoding.formato = 'R$ mil'):")
    for e in ABREVIADO:
        print(f"  {e:<24} {ABREVIADO[e]}")
    base = escala_de("custo mensal")
    print(f"\ncoerência: linha={placeholder(base)}  Total={placeholder(acima(base))}")
