"""
Project Together — powered by AI: PASSO 9 — Controlar mudanças.

Tudo muda. Projeto sério não é o que impede a mudança — é o que sabe o que ela CUSTA.
Este passo é sob demanda: rode-o sempre que receber um pedido de mudança.

ELE RODA EM TRÊS RODADAS
  1ª  você conta qual é a mudança e ESCOLHE o que a IA deve detalhar (escopo novo,
      riscos, cronograma, orçamento — cada um liga e desliga). Sai o prompt, já com a
      foto do seu quadro dentro.
  2ª  você cola a resposta ([P9] e, se pediu, [P9.2]/[P9.3]/[P9.4]) na Caixa de entrada:
      nasce o cartão MC-001 na lista "Mudanças", com o impacto e o detalhamento DENTRO
      dele. O quadro ainda NÃO cresce — escopo que ninguém autorizou é rascunho.
  3ª  você registra a DECISÃO do patrocinador. APROVADA: o detalhamento vira plano
      (escopo na EAP, riscos, datas, custos) e você captura a LINHA DE BASE nova.

PREDITIVA: impacto contra a linha de base (escopo, prazo, custo, risco).
ÁGIL:      repriorização do backlog — o que entra, e o que sai para caber.

A LINHA DE BASE, e quem manda nela: capturar uma nova é o fecho normal de uma mudança
aprovada. Descartar uma que existe é decisão do LÍDER do projeto (opção L abaixo) — o
curso mostra, pergunta e registra quem decidiu; não decide por você.
"""
import baseline
import contexto
import mudancas
import passos
import progresso
import quadro
from prompts import PARTES_MUDANCA, detalhamento_da_mudanca, nome_abordagem
from runner import copiar, entrada_longa, ok, step, warn
from idioma import T, Tf

N = 9
c, ids, abordagem = passos.abrir(N)

blocos_caixa = quadro.blocos_da_caixa(ids)
bloco = blocos_caixa.get(f"P{N}")

_ROTULO_PARTE = {"escopo": T("o ESCOPO novo (entregas e pacotes, e onde entram na EAP)"),
                 "riscos": T("os RISCOS que a mudança traz (Passo 3)"),
                 "cronograma": T("o CRONOGRAMA dos itens novos (Passo 4)"),
                 "orcamento": T("o ORÇAMENTO dos itens novos (Passo 4)")}


def _sim(pergunta, padrao=True):
    marca = "[S/n]" if padrao else "[s/N]"
    try:
        r = input(f"     {pergunta} {marca} ").strip().lower()
    except (EOFError, KeyboardInterrupt):
        return padrao
    if not r:
        return padrao
    return r in ("s", "sim", "y", "yes")


# ─────────────────────────────────────────────────────────────────────────────
# ETAPA 3 (quando há mudança esperando decisão) — decidir e aplicar
# ─────────────────────────────────────────────────────────────────────────────
def _decidir_e_aplicar():
    """Mostra as mudanças pendentes, registra a decisão do patrocinador e, se APROVADA,
    transforma o detalhamento em plano. Devolve True se fez alguma coisa."""
    pend = mudancas.pendentes(ids)
    if not pend:
        return False
    print(T("\n  MUDANÇAS ESPERANDO A DECISÃO DO PATROCINADOR:"))
    for i, card in enumerate(pend, 1):
        print(f"    {i}. {(card.get('titulo') or '')[:70]}")
    print(T("\n  Enquanto ninguém decide, o quadro não muda — é o que separa mudança de"))
    print(T("  escopo que cresce sozinho."))
    try:
        esc = input(T("\n  Registrar a decisão de qual? (número, ou Enter para pular) ")).strip()
    except (EOFError, KeyboardInterrupt):
        return False
    if not esc.isdigit() or not (1 <= int(esc) <= len(pend)):
        return False
    card = pend[int(esc) - 1]
    codigo = mudancas.codigo_do_cartao(card.get("titulo") or "") or "MC-?"

    print(T("\n  A decisão é do patrocinador — você só a registra aqui."))
    print(T("    1. APROVADA     2. REJEITADA     3. ADIADA"))
    try:
        d = input(T("  Decisão [1/2/3]: ")).strip()
        quem = input(T("  Quem decidiu? (nome) ")).strip()
        motivo = input(T("  Por quê? (uma linha) ")).strip()
    except (EOFError, KeyboardInterrupt):
        return False
    decisao = {"1": "APROVADA", "2": "REJEITADA", "3": "ADIADA"}.get(d)
    if not decisao:
        print(T("  Ok — não registrei nada."))
        return False
    mudancas.gravar_decisao(card, decisao, quem=quem, motivo=motivo)
    ok(Tf("{0}: decisão {1} registrada no cartão (quem, quando e por quê).", codigo, decisao))

    if decisao != "APROVADA":
        print(Tf('\n  {0} não entra no plano — o quadro continua como está, e o cartão', codigo))
        print(T("  guarda a decisão. (Uma mudança ADIADA pode voltar: é só registrar a"))
        print(T("  decisão de novo quando o patrocinador decidir.)"))
        return True

    detalhe, fonte = mudancas.ler_detalhe(codigo, card)
    if not detalhe:
        print(Tf('\n  {0} foi aprovada, mas não tem detalhamento guardado — ela foi', codigo))
        print(T("  registrada só com a análise de impacto. Ajuste o plano você mesmo (Passo"))
        print(T("  2/3/4) ou rode o Passo 9 de novo pedindo o detalhamento."))
        return True

    print(Tf('\n  Aplicando {0} no seu plano (detalhamento lido do {1})...', codigo, fonte))
    resumo = []
    if detalhe.get("P9.2"):
        # `confirmar=_sim` é o que autoriza a REMOÇÃO de escopo: uma mudança aprovada
        # pode tirar trabalho do plano, mas apagar cartão é irreversível — então quem
        # apaga é o aluno, respondendo, depois de ver a lista inteira do que sai.
        # padrao=False de propósito: num apagamento, Enter tem de significar NÃO.
        r = mudancas.aplicar_escopo(ids, detalhe["P9.2"], codigo, log=step,
                                    confirmar=lambda p: _sim(p, padrao=False)) or {}
        if r.get("removidos"):
            resumo.append(f"{len(r['removidos'])} item(ns) removido(s) do escopo")
        if any(r.get(k) for k in ("fases", "entregas", "pacotes")):
            resumo.append(f"{r.get('fases',0)} fase(s), {r.get('entregas',0)} entrega(s) e "
                          f"{r.get('pacotes',0)} pacote(s) na EAP")
        for antes, depois, titulo in (r.get("renumerados") or [])[:12]:
            step(Tf("  renumerado: {0} → {1}  {2}", antes, depois, titulo[:44]))
        if len(r.get("renumerados") or []) > 12:
            step(Tf("  ... e mais {0} renumerado(s).", len(r['renumerados']) - 12))
        if r.get("renumerados"):
            print(T("     (a entrega entrou no meio da EAP, então quem vinha depois subiu de"))
            print(T("      número — os documentos e os títulos das tarefas já são refeitos"))
            print(T("      abaixo; quem recebeu um documento antigo precisa do novo.)"))
    if detalhe.get("P9.3"):
        n_r = mudancas.aplicar_riscos(ids, detalhe["P9.3"], codigo, log=step)
        if n_r:
            resumo.append(f"{n_r} ponto(s) de atenção")
    if detalhe.get("P9.4"):
        datados, n_dep = mudancas.aplicar_cronograma(ids, detalhe["P9.4"], log=step)
        if datados:
            resumo.append(f"{datados} pacote(s) datado(s)")
        if n_dep:
            resumo.append(f"{n_dep} dependência(s)")
        n_o, total = mudancas.aplicar_orcamento(ids, detalhe["P9.4"], codigo, log=step)
        if n_o:
            resumo.append(f"{n_o} item(ns) de orçamento"
                          + (f" (≈ R$ {total:,.0f})".replace(",", ".") if total else ""))
    ok(Tf("{0} virou plano: {1}", codigo,
          ", ".join(resumo) if resumo else T("nada a escrever.")))

    fecho = mudancas.fechar_o_plano(ids, log=step)
    if fecho:
        ok(fecho + ".")

    # A LINHA DE BASE NOVA — o fecho de toda mudança aprovada.
    print(T("\n  Uma mudança aprovada muda a linha de base: o plano aprovado agora é OUTRO."))
    print(T("  Capturar a nova é o que faz o Passo 8 voltar a comparar com a verdade."))
    if _sim(T("Capturar a linha de base nova agora?"), padrao=True):
        info = baseline.capturar(f"Linha de base — {codigo} aprovada", ids=ids, log=step)
        if info:
            passos.escrever_cartoes(ids, "CONCLUIDO", [(
                f"📌 Linha de base — {codigo}",
                f"Linha de base capturada depois da mudança {codigo} ser APROVADA "
                f"(baseline #{info.get('baseline_id')}, {info.get('total_cartoes','?')} "
                f"cartões). É contra ela que o Passo 8 passa a medir o planejado × "
                f"realizado.", None)], log=step)
            ok(T("Linha de base nova capturada e marcada como vigente."))
    else:
        print(T("  Ok — a linha de base continua a anterior. (Capture quando quiser: aqui,"))
        print(T("  ou rodando o Passo 5 de novo.)"))
    return True


def _desfazer():
    """Escolhe uma mudança já aplicada e tira do quadro tudo o que ela escreveu.

    A porta de saída para quando o detalhamento da IA sai com a estrutura errada: sem
    ela, o aluno que rodasse o Passo 9 de novo empilharia o escopo novo em cima do
    torto — e a única alternativa era zerar o Passo 2, perdendo a EAP inteira.
    """
    aplicadas = [c for c in mudancas.todas(ids)
                 if mudancas.decisao_do_cartao(c) == "APROVADA"]
    if not aplicadas:
        print(T("\n  Nenhuma mudança APROVADA no quadro — não há aplicação para desfazer."))
        return
    print(T("\n  MUDANÇAS APROVADAS (as que escreveram no seu plano):"))
    for i, card in enumerate(aplicadas, 1):
        print(f"    {i}. {(card.get('titulo') or '')[:70]}")
    try:
        esc = input(T("\n  Desfazer qual? (número, ou Enter para sair) ")).strip()
    except (EOFError, KeyboardInterrupt):
        return
    if not esc.isdigit() or not (1 <= int(esc) <= len(aplicadas)):
        return
    codigo = mudancas.codigo_do_cartao(aplicadas[int(esc) - 1].get("titulo") or "")
    if not codigo:
        print(T("  Não consegui ler o código (MC-NNN) dessa mudança."))
        return
    n = mudancas.desfazer_aplicacao(ids, codigo, confirmar=lambda p: _sim(p, padrao=False),
                                    log=step)
    if not n:
        return
    ok(Tf("{0} desfeita: {1} cartão(ões) removido(s) do quadro.", codigo, n))
    fecho = mudancas.fechar_o_plano(ids, log=step)
    if fecho:
        ok(fecho + ".")
    print(T("\n  Agora o seu plano está como estava ANTES desta mudança. Para aplicá-la de"))
    print(T("  novo com o detalhamento corrigido: rode o Passo 9, escolha N, descreva a"))
    print(T("  mudança (agora você pode colar várias linhas) e cole a resposta nova."))


# ─────────────────────────────────────────────────────────────────────────────
# ETAPA 1 — não há resposta na caixa: montar o prompt (com os interruptores)
# ─────────────────────────────────────────────────────────────────────────────
if not bloco:
    # O Passo 9 faz TRÊS coisas, e todas são sob demanda — então ele começa perguntando
    # qual delas você quer agora. (Antes ele só sabia montar o prompt de uma mudança
    # nova; a decisão e a linha de base não tinham porta.)
    pendentes = mudancas.pendentes(ids)
    print(T("\n  O QUE VOCÊ QUER FAZER AGORA?"))
    print(T("    N. Registrar uma MUDANÇA NOVA (eu monto o prompt de análise de impacto)"))
    if pendentes:
        print(Tf("    D. Registrar a DECISÃO do patrocinador — {0} mudança(s) esperando", len(pendentes)))
    print(T("    L. LINHA DE BASE — ver as que existem e descartar (decisão do líder)"))
    print(T("    X. DESFAZER uma mudança já aplicada — tira do quadro o que ela escreveu"))
    try:
        opcao = (input(T("\n  Escolha [N/D/L/X] (Enter = N): ")).strip() or "n").lower()
    except (EOFError, KeyboardInterrupt):
        opcao = "n"

    if opcao == "l":
        baseline.escolher_e_descartar(ids)
        raise SystemExit(0)
    if opcao == "x":
        _desfazer()
        raise SystemExit(0)
    if opcao == "d" and pendentes:
        _decidir_e_aplicar()
        raise SystemExit(0)

    if 5 not in progresso.feitos():
        # A fronteira do método: sem plano aprovado, acrescentar escopo não é mudança.
        print(T("\n  ⚠️  O seu plano ainda NÃO foi aprovado (Passo 5) — então não há linha de"))
        print(T("      base para medir mudança contra."))
        print(T("      Acrescentar escopo agora é ELABORAÇÃO PROGRESSIVA, e o lugar dela é o"))
        print(T("      PASSO 2: o detalhe aparece quando você chega perto, e ninguém precisa"))
        print(T("      autorizar. Mudança começa a existir depois que o plano é aprovado."))
        if not _sim(T("Seguir mesmo assim e registrar isto como mudança?"), padrao=False):
            raise SystemExit(0)

    # A DESCRIÇÃO DA MUDANÇA É TEXTO LONGO — e por isso não pode ser um input() de uma
    # linha (Eduardo, 2026-07-30). Um pedido de mudança de verdade traz o contexto, a
    # lista do que muda e, muitas vezes, um RASCUNHO de como o escopo novo deve ficar —
    # e é justamente esse rascunho que faz a IA acertar a estrutura da EAP em vez de
    # inventar uma. Com input() de uma linha, colar isso era pior do que perder o texto:
    # cada quebra de linha virava um Enter, a 1ª linha era a mudança e as outras iam
    # sendo consumidas pelas perguntas s/n logo abaixo — o aluno "respondia" os
    # interruptores do detalhamento sem saber. O entrada_longa() lê tudo de uma vez
    # (da área de transferência ou do console, fechando com "." numa linha).
    mudanca = entrada_longa(
        T("  QUAL MUDANÇA VOCÊ RECEBEU?"),
        dica=T("   Escreva o pedido inteiro — o contexto, o que muda e, se você já tiver,\n"
             "   um RASCUNHO de como o escopo novo deve ficar (a IA segue o seu rascunho\n"
             "   em vez de inventar uma estrutura)."))

    # OS INTERRUPTORES (o pedido do Eduardo, 29/07): a IA pode entregar a mudança já
    # detalhada — e cada pedaço do detalhamento liga e desliga aqui, sem editar prompt.
    #
    # SÓ NA PREDITIVA, e o motivo é de método: no ágil a mudança NÃO vira entrega nova
    # com data e custo — ela vira ordem nova no Product Backlog, e quem a detalha é a
    # Sprint Planning (Passo 5) quando o item subir. Oferecer aqui um detalhamento em
    # forma de EAP seria ensinar a fazer preditiva com nome de ágil.
    partes = []
    if abordagem == "PREDITIVA":
        print(T("\n  O QUE VOCÊ QUER QUE A IA DETALHE (além da análise de impacto)?"))
        print(T("  Uma mudança aprovada vira plano: escopo novo, riscos, datas e custos. Peça"))
        print(T("  aqui o que quiser que já venha pronto — e desligue o que não quiser.\n"))
        partes = [p for p in PARTES_MUDANCA if _sim(_ROTULO_PARTE[p] + "?", padrao=True)]
    else:
        print(T("\n  No ágil a mudança não vira entrega com data e custo: ela vira ORDEM NOVA"))
        print(T("  no Product Backlog. Quem detalha é a Sprint Planning (Passo 5), quando o"))
        print(T("  item subir — então aqui eu peço a repriorização e o refinamento, não um"))
        print(T("  plano paralelo."))
    detalhar = detalhamento_da_mudanca(partes) or T(
        "Nesta rodada eu quero SO' a analise de impacto e a repriorizacao: nao detalhe "
        "escopo novo, riscos, cronograma nem orcamento.")

    extras = {"MUDANCA": mudanca or T("(descreva aqui a mudança que você recebeu)"),
              "DETALHAR": detalhar,
              "QUADRO": quadro.snapshot(ids)}
    texto, _faltando = contexto.corpo_prompt_passo(N, ids, extras=extras)
    contexto.gerar_prompt_pronto(N, ids, extras=extras)
    if texto and copiar(texto):
        ok(T("O prompt do PASSO 9 está na sua área de transferência — com a sua mudança, a "
           "foto do quadro e o detalhamento que você pediu."))
    else:
        ok(T("O prompt está em _saida/prompt-passo-9.txt."))
    if partes:
        _BLOCO_DA_PARTE = {"escopo": "[P9.2]", "riscos": "[P9.3]",
                           "cronograma": "[P9.4]", "orcamento": "[P9.4]"}
        rotulos = list(dict.fromkeys(_BLOCO_DA_PARTE[p] for p in partes))
        print(Tf("\n  A IA vai responder o impacto em [P9] e o detalhamento em {0}.",
                 ", ".join(rotulos)))
    print(T("\n  Cole na sua IA e peça a análise. Ela pode analisar VÁRIAS mudanças de uma"))
    print(T("  vez — cada uma começa com [P9]. Cole a resposta INTEIRA no cartão"))
    print(T("  \"0. Caixa de entrada\" e rode o Passo 9 de novo: eu registro cada mudança"))
    print(T("  como um cartão na lista \"Mudanças\" — e o detalhamento fica guardado nele,"))
    print(T("  esperando a decisão do patrocinador."))
    raise SystemExit(0)


# ─────────────────────────────────────────────────────────────────────────────
# ETAPA 2 — a IA respondeu: registrar a(s) mudança(s), SEM mexer no plano
# ─────────────────────────────────────────────────────────────────────────────
# A IA volta e meia analisa VARIAS mudancas de uma vez — cada uma num bloco [P9]. Antes,
# so' a ULTIMA era registrada (o dict da caixa colapsava rotulos iguais); o Eduardo
# mandou 4 e o quadro guardou 1 (2026-07-15). Por isso lemos TODOS os blocos [P9].
blocos = quadro.blocos_multiplos_da_caixa(ids, f"P{N}") or [bloco]

# o detalhamento vem em blocos irmãos ([P9.2] escopo, [P9.3] riscos, [P9.4] Passo 4) —
# rótulos próprios de propósito: um [P2] na caixa faria o Passo 2 reescrever o plano
# inteiro na próxima vez que ele rodasse.
detalhe = {r: t for r, t in blocos_caixa.items() if r.startswith("P9.")}
partes_lidas = [{"P9.2": "escopo", "P9.3": "riscos", "P9.4": "cronograma/orçamento"}[r]
                for r in sorted(detalhe) if r in ("P9.2", "P9.3", "P9.4")]

cartoes, codigos = [], []
for i, b in enumerate(blocos):
    codigo = mudancas.proximo_codigo(ids) if i == 0 else \
        f"MC-{int(codigos[-1].split('-')[1]) + 1:03d}"
    # o detalhamento pertence à PRIMEIRA mudança do lote: uma resposta que analisa 4
    # mudanças e detalha uma só não deixa dúvida sobre de quem é o escopo novo.
    meu_detalhe = detalhe if i == 0 else {}
    titulo, desc = mudancas.texto_do_cartao_da_mudanca(codigo, b, meu_detalhe, partes_lidas)
    if meu_detalhe:
        mudancas.guardar_detalhe(codigo, meu_detalhe)
    cartoes.append((titulo, desc))
    codigos.append(codigo)

n = mudancas.registrar(ids, cartoes, log=step)
ok(Tf("{0} mudança(s) registrada(s) na lista \"Mudanças\": {1}.",
      len(cartoes), ", ".join(codigos)))
if detalhe:
    ok(Tf("O detalhamento ({0}) ficou guardado em {1} — ele vira plano quando a mudança for "
          "APROVADA, e não antes.", ', '.join(partes_lidas), codigos[0]))

# só os blocos do DETALHAMENTO saem aqui; o [P9] quem consome é o passos.finalizar (o
# dono dele) — consumir os dois aqui fazia a Caixa ser "distribuída e limpa" duas vezes
# na mesma rodada, com duas mensagens contraditórias na tela.
if detalhe:
    quadro.consumir_blocos(ids, list(detalhe), log=step)

# Artefato do Passo 9: o Registro das solicitações de mudanças (.xlsx), lido do quadro.
try:
    import artefatos
    cam, _av = artefatos.gerar_registro_mudancas(ids, abordagem=abordagem, log=step)
    if cam:
        ok(T("Registro das solicitações de mudanças gerado em _saida/documentos/ (.xlsx)."))
except Exception as e:
    warn(Tf("não consegui gerar o Registro das solicitações de mudanças agora: {0}", e))

print(T("\n  O QUADRO AINDA NÃO MUDOU — e isso é de propósito: quem autoriza escopo novo é"))
print(T("  o patrocinador. Rode o Passo 9 de novo para registrar a decisão dele; se for"))
print(T("  APROVADA, eu aplico o detalhamento no plano e capturo a linha de base nova."))
print(T("\n  (Precisa mexer nas linhas de base já congeladas? A decisão é do líder do"))
print(T("   projeto: rode o Passo 9 e escolha \"L\" quando não houver mudança pendente.)"))

passos.finalizar(
    N, ids,
    resumo=Tf("{0} mudança(s) analisada(s) e registrada(s) ({1}).",
              len(cartoes), nome_abordagem(abordagem)),
    mensagem_conclusao=Tf("Passo 9 concluído — {0} mudança(s) no quadro, com o impacto visível "
                          "e a decisão pendente. Controlar mudança é rotina: rode quando "
                          "surgir outra (a IA pode analisar várias de uma vez).", len(cartoes)))
