"""
Project Together — powered by AI: PASSO 2 — Revisar o escopo.

A MESMA PERGUNTA, DUAS FORMAS
  O escopo responde ao O QUE do 5W2H. No passo 1 voce listou as ENTREGAS (o que
  precisa existir). Agora voce as abre no nivel em que o trabalho acontece:

  ABORDAGEM PREDITIVA -> EAP (Estrutura Analitica do Projeto)
      Cada entrega vira um cartao PAI; cada pacote de trabalho, uma SUBTAREFA.
      Pacote de trabalho = o menor pedaco que da para estimar, atribuir e
      verificar como pronto. Regra: 100% do escopo, e SO o escopo.

  ABORDAGEM AGIL -> Product Backlog
      Pelo Guia PMBOK 8, o backlog e "uma lista dinamica e priorizada de itens de
      trabalho e funcionalidades" focada em RESULTADOS ORIENTADOS POR VALOR. Cada
      item e um RESULTADO que uma parte interessada passa a ter — o Guia define
      historia de usuario como "uma descricao breve de um resultado sob a
      perspectiva de uma parte interessada", NAO uma funcionalidade de sistema.
      So' e software se a sua justificativa disser que o produto e um software;
      senao, o item e um resultado da vida real (um habito, uma rotina, um evento,
      um espaco entregue). Priorizado por VALOR, com criterio de pronto. O escopo
      nao esta errado por mudar — ele e feito para mudar.

  Nos dois casos o cartao e o mesmo objeto: e o quadro que muda de forma, porque
  a abordagem mudou. Esse e o ponto do curso.

Este script le a ABORDAGEM do .env e preenche a lista de escopo certa.
Idempotente: nao duplica cartoes ja criados (compara pelo titulo).

O RITMO (o mesmo de todo passo, modo CHAT):
  1. o prompt do Passo 2 ja foi para a sua area de transferencia no fim do Passo 1
     (e esta no cartao "02 - Passo 2" da lista Referencias);
  2. cole na sua IA; ela responde um bloco que comeca com [P2];
  3. cole a resposta INTEIRA no cartao "0. Caixa de entrada" do seu quadro;
  4. rode este passo -> ele LE o quadro e monta o escopo. Voce nao edita arquivo nenhum.
"""
from env import cfg, require, get_int
from pa_client import api, list_cards, create_card, update_card, ordenar_cartoes
from runner import header, ok, step, warn, done, copiar
import contexto
import entradas
import progresso
import prompts
import quadro
from quadro import (garantir_listas, abordagem_do_env, blocos_da_caixa,
                    consumir_blocos)
from idioma import T, Tf

# ─────────────────────────────────────────────────────────────────────────────
# >>> VALORES INICIAIS (o curso le tudo do QUADRO; isto e' so o ponto de partida)
#
#  ABORDAGEM PREDITIVA — preencha EAP.
#     Repita o nome da entrega em cada pacote dela (o script monta a hierarquia).
EAP = [
    # {"entrega": "", "pacote": "", "aceitacao": ""},
]

#  ABORDAGEM AGIL — a META DO PRODUTO e o BACKLOG que a persegue.
#     A Meta do Produto e' o COMPROMISSO do Product Backlog (Guia Scrum): o estado que,
#     atingido, permite encerrar o projeto. UMA frase, um ESTADO (nao uma atividade).
META_PRODUTO = ""

#  ABORDAGEM AGIL — preencha BACKLOG (prioridade 1 = mais alta; na ordem).
#     Cada item e um RESULTADO de valor (ex.: "Rotina de forca 2x/semana em pratica").
#     Historia de usuario ("Como <quem>, quero <o que>, para <valor>") so' se voce
#     escolheu esse formato no Passo 1 (01a) — ela nao e obrigatoria.
BACKLOG = [
    # {"historia": "Rotina de forca 2x/semana em pratica",
    #  "aceitacao": "", "pontos": 3, "prioridade": 1},
]

# <<< FIM DOS VALORES INICIAIS
# ─────────────────────────────────────────────────────────────────────────────

c = cfg()
require(c, "PROJECTADM_TOKEN", "BOARD_ID")
abordagem = abordagem_do_env()
if not abordagem:
    raise SystemExit(T("ERRO: rode 01a_escolher_abordagem.py primeiro (ABORDAGEM no .env)."))

header(Tf("Passo 2 — Revisar o escopo  ({0})", prompts.nome_abordagem(abordagem)))
ids = garantir_listas(abordagem, log=lambda _m: None)   # garante as listas, em silencio
lista_escopo = ids["ESCOPO"]

# DE ONDE VEM A SUA RESPOSTA — a mesma porta de sempre: a CAIXA DE ENTRADA do quadro.
# O aluno CHAT nao abre arquivo nenhum: ele cola a resposta da IA (o bloco [P2]) no
# cartao "0. Caixa de entrada" e roda este passo. Antes, este script so' lia a ZONA DE
# EDICAO deste .py — e quem estava no CHAT batia em "ERRO: a lista EAP esta vazia"
# sem ter, em lugar nenhum, onde colar a resposta (Eduardo, 2026-07-14).
if True:
    bloco = blocos_da_caixa(ids).get("P2")
    if bloco:
        step(T("Modo CHAT — lendo a sua resposta da Caixa de entrada (bloco [P2])..."))
        # Já concluiu o Passo 2 antes? Pergunta se é para apagar o escopo da rodada
        # anterior antes de escrever este (senão os dois convivem no quadro).
        import passos as _passos
        _passos.confirmar_regeracao(2, ids, abordagem)
        if abordagem == "PREDITIVA":
            EAP = entradas.eap(bloco) or EAP
            ok(Tf("li {0} pacote(s) de trabalho do quadro.", len(EAP)))
        else:
            BACKLOG = entradas.backlog(bloco) or BACKLOG
            META_PRODUTO = entradas.meta_produto(bloco) or META_PRODUTO
            ok(Tf("li {0} história(s) do quadro.", len(BACKLOG)))
            if META_PRODUTO:
                ok(T("li a Meta do Produto."))
        consumir_blocos(ids, ["P2"], log=step)

existentes = {(cd.get("titulo") or ""): cd for cd in list_cards(lista_escopo)}


def _ja(titulo):
    return existentes.get(titulo)


def _sem_resposta(o_que):
    raise SystemExit(
        Tf('Ainda não há {0} para colocar no quadro.\n \n   O prompt do Passo 2 já está '
           'pronto, com o SEU projeto dentro — ele foi para\n   a sua área de transferência '
           'quando você terminou o Passo 1 (e está no cartão\n   "02 - Passo 2" da lista '
           'Referências).\n \n   1. Cole o prompt na sua IA.\n   2. Copie a resposta INTEIRA '
           'dela (ela começa com [P2]).\n   3. Cole no cartão "0. Caixa de entrada" do seu '
           'quadro.\n   4. Rode o Passo 2 de novo — eu leio o quadro e monto o escopo.', o_que))


if abordagem == "PREDITIVA":
    if not EAP:
        _sem_resposta("nenhum pacote de trabalho (EAP)")

    def _novo(titulo, desc, pai=None):
        """Cria um cartão (opcionalmente como subtarefa de `pai`) e devolve o id."""
        corpo = {"lista_id": lista_escopo, "titulo": titulo, "descricao": desc}
        if pai:
            corpo["cartao_pai_id"] = pai
        return api("POST", "/cartoes", corpo)["dados"]["id"]

    # EAP em até 3 níveis (PMBOK 8, Fig. 5-7): FASE (nível 1, OPCIONAL) > ENTREGA
    # (nível 2) > PACOTE DE TRABALHO (nível 3). Sem fases, fica em 2 níveis.
    # A ordem de leitura é preservada; entrega/fase vêm do carry-forward do parser.
    fases_ordem, entregas_ordem = [], []          # entregas_ordem = [(entrega, fase)]
    vf, ve = set(), set()
    for p in EAP:
        f = (p.get("fase") or "").strip()
        e = (p.get("entrega") or "").strip()
        if f and f not in vf:
            vf.add(f); fases_ordem.append(f)
        if e and e not in ve:
            ve.add(e); entregas_ordem.append((e, f))
    tem_fases = bool(fases_ordem)

    # Nível 1 — FASES (só quando o projeto tem fases).
    fase_id = {}
    if tem_fases:
        step(Tf("{0} fase(s) · {1} entrega(s) · {2} pacote(s)", len(fases_ordem), len(entregas_ordem), len(EAP)))
        for f in fases_ordem:
            cd = _ja(f)
            if cd:
                fase_id[f] = cd["id"]; ok(Tf("[já existe] fase: {0}", f))
            else:
                fase_id[f] = _novo(f, T("Fase do ciclo de vida (PMBOK 8): agrupa as "
                                        "entregas desta etapa. As entregas desta fase são "
                                        "as subtarefas deste cartão."))
                ok(Tf("[criada]    fase: {0}", f))
    else:
        step(Tf("{0} entrega(s) -> {1} pacote(s) de trabalho", len(entregas_ordem), len(EAP)))

    # Nível 2 — ENTREGAS (subtarefas da fase; ou topo da lista, sem fase).
    entrega_id = {}
    for e, f in entregas_ordem:
        cd = _ja(e)
        if cd:
            entrega_id[e] = cd["id"]; ok(Tf("[já existe] entrega: {0}", e))
        else:
            entrega_id[e] = _novo(
                e, T("Entrega do termo de abertura (passo 1). Os pacotes de trabalho "
                     "desta entrega são as subtarefas deste cartão."), fase_id.get(f))
            ok(Tf("[criada]    entrega: {0}", e))

    # Nível 3 — PACOTES DE TRABALHO (subtarefas da entrega).
    criados = 0
    for p in EAP:
        titulo = (p.get("pacote") or "").strip()
        if not titulo:
            continue
        if _ja(titulo):
            step(Tf("[já existe] pacote: {0}", titulo))
            continue
        desc = Tf("Critério de aceitação: {0}\n\n"
                  "No passo 4 este pacote ganha a estimativa de duração, as datas e o "
                  "responsável; no passo 7 ele é executado.", p.get('aceitacao', ''))
        _novo(titulo, desc, entrega_id.get((p.get("entrega") or "").strip()))
        ok(Tf("[criado]    pacote: {0}", titulo))
        criados += 1

    contexto.salvar(eap=EAP)
    if tem_fases:
        print(Tf('\nEAP: {0} fase(s) · {1} entrega(s) · {2} pacote(s) de trabalho.', len(fases_ordem), len(entregas_ordem), len(EAP)))
    else:
        print(Tf('\nEAP: {0} entrega(s) · {1} pacote(s) de trabalho.', len(entregas_ordem), len(EAP)))
    print(T("Confira: os pacotes somados entregam 100% do escopo — e só o escopo?"))
    print(T("(O QUANTO TEMPO cada pacote leva você estima no passo 4 — não aqui.)"))

else:
    if not BACKLOG:
        _sem_resposta("nenhuma história no Product Backlog")

    # ── A META DO PRODUTO — o compromisso do Product Backlog (Guia Scrum) ────────
    # Ela vem ANTES dos itens porque e' contra ela que a ordem deles se justifica.
    # UMA por projeto e sem numero (a Meta da SPRINT e' que nasce a cada Sprint), e
    # o titulo carrega a frase inteira para ser lida de relance no quadro.
    meta_id = None
    meta_card = quadro.cartao_da_meta_do_produto(ids)
    if META_PRODUTO:
        titulo_meta = f"{quadro.META_PRODUTO_PREFIXO}: {META_PRODUTO}"[:250]
        desc_meta = T(
            "O COMPROMISSO DESTE BACKLOG. Pelo Guia Scrum, cada artefato tem um "
            "compromisso: o do Product Backlog é a Meta do Produto, o do Sprint Backlog "
            "é a Meta da Sprint e o do Incremento é a Definição de Pronto.\n\n"
            "Ela é o estado que, atingido, permite ENCERRAR o projeto — e é o que os "
            "Key Results do cartão 1.2 medem. Use-a como régua: todo item abaixo "
            "responde 'isto me aproxima da meta?'. O que não aproxima sai do backlog "
            "(passo 9).\n\n"
            "É UMA só e muda pouco: se ela mudar, o projeto virou outro.")
        if meta_card:
            update_card(meta_card["id"], titulo=titulo_meta, descricao=desc_meta)
            meta_id = meta_card["id"]
            step(T("[atualizada]  Meta do Produto"))
        else:
            meta_id = create_card(lista_escopo, titulo_meta, desc_meta)
            ok(T("[criada]      Meta do Produto"))
        print(Tf("\n🏁 Meta do Produto: {0}\n", META_PRODUTO))
    elif meta_card:
        # A IA nao a devolveu nesta rodada, mas ela ja existe: MANTER. O texto do aluno
        # nunca e' sobrescrito por uma resposta que veio sem o campo.
        meta_id = meta_card["id"]
        step(T("Meta do Produto: mantida a que já estava no quadro."))
    else:
        # Nao inventamos uma meta. Avisamos — e o passo segue, porque um backlog sem
        # meta ainda e' melhor do que nenhum backlog; a conferencia cobra depois.
        warn(T("a sua IA não devolveu a META DO PRODUTO (o compromisso do Product "
               "Backlog). O backlog vai ser criado assim mesmo, mas sem ela não há "
               "contra o que ordenar os itens — peça-a à IA e cole de novo, ou "
               "escreva-a você e rode o passo outra vez."))

    itens = sorted(BACKLOG, key=lambda h: int(h.get("prioridade") or 999))
    step(Tf("{0} item(ns) no Product Backlog (ordenados por prioridade)", len(itens)))
    novos = []
    for h in itens:
        nome = (h.get("historia") or "").strip()
        if not nome:
            continue
        # O código (B1, B2…) é uma alça estável — vai no TÍTULO para os próximos passos
        # se referirem ao item por ele (e mantém o título curto, longe do limite da API).
        codigo = (h.get("codigo") or "").strip()
        titulo = f"{codigo}: {nome}" if codigo else nome
        desc = (Tf("Critérios de aceitação:\n{0}\n\nPontos: {1} · Prioridade: {2}\n\n",
                   h.get('aceitacao', ''), h.get('pontos', '?'), h.get('prioridade', '?'))
                + (Tf("{0} é o código deste item — use-o para se referir a ele nos "
                      "próximos passos (riscos, Sprints, execução).\n", codigo)
                   if codigo else "")
                + T("No passo 4 este item entra numa Sprint; no passo 7 o time o "
                    "quebra em tarefas."))
        cd = _ja(titulo)
        if cd:
            update_card(cd["id"], descricao=desc)
            step(Tf("[atualizada] {0}", titulo[:60]))
            novos.append(cd["id"])
        else:
            novos.append(create_card(lista_escopo, titulo, desc))
            ok(Tf("[criada]     {0}", titulo[:60]))
    # A ordem do backlog E a prioridade: reordena a lista de fato. A Meta do Produto vai
    # na FRENTE de tudo — ela e' a regua contra a qual essa ordem se justifica, e quem
    # abre a lista tem de ler a meta antes do primeiro item.
    # DUAS armadilhas, as duas descobertas em 09/08/2026 rodando no quadro 326:
    #
    # 1. O ENDPOINT espera {"cartoes": [...]}, nao {"ordem": [...]}. Este passo mandava
    #    `ordem` desde sempre — a API aceitava a chamada e a lista NAO se reordenava.
    #    A prioridade que o aluno via era a ordem de criacao, nao a dele. Usar
    #    `ordenar_cartoes` (o helper do pa_client) e' o que impede a proxima repeticao:
    #    o nome do campo passa a existir num lugar so'.
    # 2. Os ids vem de origens com TIPOS diferentes — `create_card` devolve INTEIRO e
    #    `list_cards` devolve STRING. Misturados, o mesmo cartao vai duas vezes e a API
    #    recusa a lista inteira ("IDs duplicados").
    #
    # A lista mandada e' a lista INTEIRA (inclusive o cartao-guia, no fim): mandar so' uma
    # parte deixa o resto onde estava, e a Meta do Produto acabava ABAIXO do guia.
    ordem = [str(x) for x in (([meta_id] if meta_id else []) + novos)]
    resto = [str(c["id"]) for c in list_cards(lista_escopo) if str(c["id"]) not in ordem]
    ordem = list(dict.fromkeys(ordem + resto))   # preserva a ordem, tira repetido
    try:
        ordenar_cartoes(lista_escopo, ordem)
        ok(T("Backlog reordenado por prioridade (o topo é o que vem primeiro)."))
    except Exception as e:
        warn(Tf("não consegui reordenar a lista: {0}", e))
    contexto.salvar(backlog=BACKLOG, meta_produto=META_PRODUTO)
    pontos = sum(int(h.get("pontos") or 0) for h in itens)
    print(Tf('\nBacklog: {0} histórias · {1} ponto(s).', len(itens), pontos))
    print(T("Confira: o item do topo é mesmo o de maior valor (ou o que ensina mais cedo)?"))

caminho = contexto.gerar_prompt_pronto(3, ids)
progresso.registrar(2, Tf("Escopo definido na abordagem {0}.",
                          prompts.nome_abordagem(abordagem)))

done(T("Passo 2 concluído — o escopo está no quadro."))

# O grande valor: o quadro JA e a EAP. Entao geramos o DICIONARIO DA EAP (Word + Excel,
# no visual do template do curso) lendo o quadro — voce nao copia nada. Subproduto de
# concluir o passo (como o Termo de Abertura no Passo 1). So' preditiva (a EAP); nunca
# derruba o passo. Regenere quando quiser pela tecla D do menu.
if abordagem == "PREDITIVA":
    # O CÓDIGO DA EAP no cartão, em CAMPO PRÓPRIO ("Código EAP") — não no título nem
    # dentro da descrição (Eduardo, 2026-07-29). O ProjectAdm não tem campo nativo para
    # ele: a hierarquia mora em cartao_pai_id + ordem, e o número é calculado por nós.
    try:
        import campos as _campos
        _nos, _grav = _campos.sincronizar_codigos(ids, log=step)
        if _grav:
            ok(Tf('Código da EAP gravado em {0} cartão(ões), no campo "Código EAP" (abra um '
                  'cartão do escopo: o código está lá, num campo só dele).', _grav))
    except Exception as e:
        warn(Tf("não consegui gravar os códigos da EAP agora ({0}).", e))

    try:
        import os as _os
        import artefatos
        saidas, _av = artefatos.gerar_dicionario_da_eap(ids, abordagem=abordagem)
        for cam in (saidas or []):
            print(Tf("  📄 Dicionário da EAP: _saida/documentos/{0}", _os.path.basename(cam)))
        if saidas:
            print(T("     (Word e Excel, com a numeração e a hierarquia da sua EAP — pronto"))
            print(T("      para compartilhar; regenere pela tecla D do menu ou editando os cartões)."))
    except Exception as e:
        warn(Tf("não consegui gerar o Dicionário da EAP agora ({0}); gere pela tecla D do menu "
                "quando quiser.", e))

# Conferencias automaticas do Passo 2 (escopo): ajuda a ver o que ainda falta na EAP —
# entregas distribuidas, decompostas em pacotes, com criterio de aceitacao — e marca no
# quadro os cartoes a revisar. Nao trava.
try:
    import passos
    passos.conferir(2, ids, abordagem)
except Exception as e:
    warn(Tf("não consegui rodar as conferências automáticas do Passo 2 agora ({0}).", e))

prompt3, faltando3 = contexto.corpo_prompt_passo(3, ids)
if prompt3 and not faltando3 and copiar(prompt3):
    print(T("✅ O prompt do PASSO 3 (riscos) já está na sua área de transferência — com o"))
    print(T("   seu escopo dentro. Cole na sua IA quando começar o Passo 3."))
elif caminho:
    # sem o numero na frase — ver o comentario igual no 01_iniciar_projeto.py
    print(T("O prompt do PRÓXIMO PASSO (riscos) já está pronto: _saida/prompt-passo-3.txt"))
