"""
Project Together — powered by AI: PASSO 6 — Engajar as partes interessadas.

Projeto não morre de cronograma. Morre de gente que não foi ouvida. Para cada parte
interessada (que você mapeou no Passo 1), este passo registra: de onde ela está
(resistente / neutro / apoiador / líder) para onde precisa estar e a estratégia
concreta para levá-la até lá — uma ação concreta que, quando faz sentido, já traz
embutida a frequência e o rito da conversa, sempre com um objetivo de DECISÃO.

O plano de engajamento entra na DESCRIÇÃO de cada cartão da lista Partes Interessadas
(o quadro já tem a parte; aqui ela ganha o "como engajar").

O RITMO (modo CHAT): prompt do Passo 6 → IA → resposta ([P6]) na Caixa de entrada →
rode este passo.
"""
import re

import entradas
import passos
import quadro
import vocabulario
from pa_client import list_cards, update_card
from runner import ok, step, warn
from idioma import T, Tf

N = 6
c, ids, abordagem = passos.abrir(N)
bloco = passos.ler_bloco(ids, N, T("nenhum plano de engajamento"))

planos = entradas.itens(bloco)
if not planos:
    raise SystemExit(T("Não consegui ler nenhuma parte no bloco [P6]. Confira o formato "
                     "(cada parte num •, com atual/desejado/estratégia)."))


def _chave(nome):
    """Nome da parte -> chave para casar com o cartao, tolerante ao que a IA acrescenta.

    A IA volta e meia devolve a parte com a categoria colada no nome ('Eu (você)
    [cliente]') — porque e assim que o Passo 1 pede as partes. O cartao, porem, se
    chama so 'Eu (você)'. Sem tirar esse '[cliente]', a parte nunca casava e o plano de
    engajamento se perdia (Eduardo, 2026-07-15). Removemos qualquer '[...]' no fim antes
    de comparar — e comparamos sem acento/caixa."""
    nome = re.sub(r"\s*\[[^\]]*\]\s*$", "", nome or "").strip()
    return entradas._sem_acento(nome)


# Palavras que não identificam ninguém — ficam de fora da comparação por palavras.
_VAZIAS = {"de", "do", "da", "dos", "das", "e", "o", "a", "os", "as", "que", "em", "no",
           "na", "nos", "nas", "para", "por", "com", "um", "uma", "ao", "aos", "eu"}


def _palavras(ch):
    return {p for p in re.split(r"[^0-9a-z]+", ch) if len(p) > 1 and p not in _VAZIAS}


def _parear(ch, cartoes, usados):
    """O cartão de Partes que corresponde a esta parte do plano — ou None.

    Casar por texto exato (ou por 'um contém o outro') não basta: a IA escreve a parte com
    as MESMAS palavras em outra ordem. No board 270 (Eduardo, 23/07) o plano dizia
    "Eduardo (Idealizador, patrocinador e instrutor)" e o cartão se chama "Idealizador,
    patrocinador e instrutor (Eu - Eduardo)" — mesma pessoa, zero correspondência literal,
    e o plano de engajamento se perdia com um "[sem par]".

    Então comparamos por PALAVRAS: quanto do nome menor está dentro do maior. Para não
    casar errado, o vencedor precisa (a) cobrir pelo menos 60% e (b) estar bem à frente do
    segundo colocado — 'Equipe de suporte / produção (Claude - Edu)' e 'Equipe de suporte
    do ProjectAdm (...Claude - Henry)' dividem três palavras e não podem se confundir. Sem
    folga clara, devolvemos None: melhor um "[sem par]" honesto do que escrever o plano de
    uma parte no cartão de outra. E cada cartão só casa UMA vez."""
    livres = {k: cd for k, cd in cartoes.items() if id(cd) not in usados}
    if not livres:
        return None
    if ch in livres:
        return livres[ch]
    contidos = [cd for k, cd in livres.items() if k and (k in ch or ch in k)]
    if len(contidos) == 1:
        return contidos[0]
    alvo = _palavras(ch)
    if not alvo:
        return None
    notas = []
    for k, cd in livres.items():
        outras = _palavras(k)
        if not outras:
            continue
        notas.append((len(alvo & outras) / min(len(alvo), len(outras)), cd))
    if not notas:
        return None
    notas.sort(key=lambda t: t[0], reverse=True)
    melhor = notas[0][0]
    segundo = notas[1][0] if len(notas) > 1 else 0.0
    return notas[0][1] if (melhor >= 0.6 and melhor - segundo >= 0.15) else None


# O CABEÇALHO DO PLANO É TAMBÉM UM MARCADOR: este passo corta por ele para SUBSTITUIR o
# plano anterior, em vez de empilhar outro embaixo. Como o marcador fica escrito no CARTÃO,
# o corte tem de reconhecer os DOIS idiomas — quadro montado pelo pacote português e aberto
# pelo inglês (ou o contrário) é a mesma armadilha que o `idioma_guardas.py` cobra.
_MARCAS_PLANO = ("── Plano de engajamento", "── Engagement plan")


def _sem_plano_antigo(base):
    """O texto do cartão SEM o plano de engajamento de uma rodada anterior (em qualquer
    idioma). Sem isto, rodar o Passo 6 duas vezes deixava dois planos no mesmo cartão."""
    corte = len(base)
    for marca in _MARCAS_PLANO:
        i = base.find(marca)
        if i >= 0:
            corte = min(corte, i)
    return base[:corte].rstrip()


# Casa cada plano com o cartão da parte (por título, tolerante a acento/caixa/categoria).
cartoes = {_chave(cd.get("titulo", "")): cd
           for cd in list_cards(ids["PARTES"])
           if not (cd.get("titulo") or "").startswith(quadro.PROTEGIDOS_PARTES)}

atualizados = sem_par = 0
usados = set()                       # cartão já casado não recebe um segundo plano
for p in planos:
    nome = re.sub(r"\s*\[[^\]]*\]\s*$", "", p.get("_", "") or "").strip()
    # "atual: X → desejado: Y" pode vir como um campo só ('atual') com a seta dentro
    atual = p.get("atual", "")
    desejado = p.get("desejado", "")
    if "→" in atual or "->" in atual:
        partes_ = atual.replace("->", "→").split("→")
        atual = partes_[0].strip()
        desejado = desejado or (partes_[1].strip() if len(partes_) > 1 else "")
    # ... e o lado direito da seta vem com o RÓTULO grudado ("desired: leading"), que o
    # template abaixo escreve de novo — o cartão saía "desejado: desejado: líder"
    # (Eduardo, quadro 324/FIRE, 06/08/2026). Tira o rótulo, nos dois idiomas.
    desejado = re.sub(rf"^(?:\s*(?:{vocabulario.ROTULOS_PARTE['desejado']})\s*:\s*)+",
                      "", desejado, flags=re.I).strip()
    atual = re.sub(rf"^(?:\s*(?:{vocabulario.ROTULOS_PARTE['atual']}|atual|current)\s*:\s*)+",
                   "", atual, flags=re.I).strip()
    # A estratégia já embute a cadência (frequência + rito) quando faz sentido — não há
    # mais um campo "Cadência" separado (essa informação, quando necessária, vive na
    # estratégia). O parser ainda tolera um 'cadencia:' que a IA por acaso devolva.
    estrategia = p.get("estrategia", "") or T("(a definir)")
    if p.get("cadencia"):
        estrategia = Tf("{0} (cadência: {1})", estrategia, p['cadencia'])
    plano_txt = Tf("── Plano de engajamento (Passo 6) ──\n"
                   "Engajamento atual: {0} → desejado: {1}\n"
                   "Estratégia: {2}", atual or '?', desejado or '?', estrategia)
    ch = _chave(nome)
    alvo = _parear(ch, cartoes, usados)
    if not alvo:
        sem_par += 1
        step(Tf('[sem par] "{0}" não bate com nenhum cartão de Partes — confira o nome ou crie '
                'a parte no Passo 1.', nome[:40]))
        continue
    usados.add(id(alvo))
    titulo_cartao = (alvo.get("titulo") or "").strip()
    base = entradas.texto_do_cartao(alvo.get("descricao", ""))
    novo = _sem_plano_antigo(base)
    try:
        update_card(alvo["id"], descricao=(novo + "\n\n" + plano_txt).strip())
        atualizados += 1
        if _chave(titulo_cartao) != ch:      # casou por semelhança: mostre em quem escrevi
            ok(Tf('engajamento definido: {0} → cartão "{1}"', nome[:36], titulo_cartao[:36]))
        else:
            ok(Tf("engajamento definido: {0}", nome[:44]))
    except Exception as e:
        warn(Tf('não consegui atualizar "{0}": {1}', nome[:34], e))

if sem_par:
    step(Tf("{0} parte(s) do plano não tinham cartão — reveja os nomes com o Passo 1.", sem_par))

# Artefato do Passo 6: o Registro das partes interessadas (.xlsx) — identificação + engajamento.
try:
    import artefatos
    cam, _av = artefatos.gerar_registro_partes(ids, abordagem=abordagem, log=step)
    if cam:
        ok(T("Registro das partes interessadas gerado em _saida/documentos/ — identificação "
           "(poder × interesse) e o plano de engajamento de cada parte."))
except Exception as e:
    warn(Tf("não consegui gerar o Registro das partes interessadas agora: {0}", e))

passos.finalizar(
    N, ids,
    resumo=Tf("Engajamento definido para {0} parte(s) interessada(s).", atualizados),
    mensagem_conclusao=T("Passo 6 concluído — cada parte interessada tem, no seu cartão, "
                       "o plano de como levá-la de onde está aonde precisa estar."))
