# -*- coding: utf-8 -*-
"""
Agile Together — a resposta da IA entra pelo QUADRO, nao pelo arquivo .py.

POR QUE ISTO EXISTE (30/08/2026)
  Ate aqui o aluno colava a resposta da IA DENTRO do script, na "ZONA DE EDICAO".
  Isso obriga um profissional de projetos a editar Python — friccao que nao ensina
  nada sobre Scrum, e que so' funciona com uma IA capaz de editar arquivos. O BPM
  Together e o Project Together AI ja' tinham feito esta mudanca; o Agile Together
  era o ultimo curso da familia fora do padrao.

  O ciclo agora e' sempre o mesmo, em qualquer passo que use IA:

      roda o passo   ->  ele ENTREGA o prompt (area de transferencia + tela)
      cola na sua IA ->  ela responde
      cola a resposta no cartao "0. Caixa de entrada" do seu quadro
      roda o MESMO passo de novo  ->  ele LE o quadro e segue

  Rodar o passo duas vezes e' seguro por desenho: enquanto a resposta nao estiver no
  cartao, ele apenas devolve o prompt. Nada e' perdido, nada e' duplicado.

O FORMATO
  Blocos com rotulo em MAIUSCULAS, um por linha, e itens em "• ...":

      META DO PRODUTO: Correr 5 km em menos de 30 minutos ate' dezembro
      HISTORIAS:
      • Almocar proteina magra com legumes | prioridade: Alta | pontos: 2
      • Correr 3x por semana                | prioridade: Alta | pontos: 3

  O parser e' o do kit (entradas.py): tolera "-" ou "*" no lugar de "•", aceita
  acento ou nao, ignora linha vazia e cabecalho solto, e limpa o **markdown** que
  quase toda IA insiste em mandar.
"""
import os
import sys

import entradas
from pa_client import (list_lists, find_list_id_by_name, create_list, list_cards,
                       create_card, update_card, move_card, ordenar_cartoes,
                       delete_list)
from runner import copiar, step, warn
from idioma import T, Tf
import listas as L

# ── ONDE O CARTAO MORA (mudou em 31/08/2026) ─────────────────────────────────
# Eduardo, validando o curso no quadro 358 ("Envelhecer bem"):
#     "As listas de referencia e caixa de entrada ficaram a direita no final.
#      Minha sugestao e' que o card caixa de entrada seja o primeiro da lista de
#      referencia e nao exista a lista Caixa de entrada."
#
# Ele esta' certo, e o motivo e' o desenho do quadro: uma LISTA do Scrum e' um
# ESTADO do trabalho (Product Backlog -> Sprint Backlog -> Concluido). A Caixa de
# entrada nao e' estado nenhum — e' uma superficie de apoio, igual a Referencia.
# Ter uma lista inteira para um cartao so' empurrava as duas para o fim da rolagem
# horizontal, e o aluno tinha de sair procurando justamente o cartao que ele usa em
# TODOS os passos.
#
# Agora ele e' o PRIMEIRO cartao da Referencia: o playbook comeca por onde a
# resposta da IA entra. A lista antiga e' aposentada sozinha (ver `garantir`) —
# ninguem precisa arrumar o quadro na mao.
LISTA = L.REFERENCIA
# As grafias que a Referencia pode ter no quadro (o curso ja criou
# "Referência" com acento). Em ingles so existe a forma canonica.
LISTA_ALTERNATIVAS = L.REFERENCIA_ALTERNATIVAS
LISTA_ANTIGA = L.LISTA_CAIXA_ANTIGA
CARTAO = L.CARTAO_CAIXA

# O texto de fabrica do cartao. Sai do catalogo pelo T(): e o que o aluno LE
# dentro do cartao, e num pacote em ingles ele tem de nascer em ingles.
_GUIA = (T("Cole AQUI a resposta da sua IA e rode o passo de novo.\n\n"
           "O script le este cartao, leva o resultado para o quadro e limpa o caminho.\n"
           "Pode colar uma resposta por vez — a ultima e' a que vale."))

# ⚠️ O QUE SEPARA "o aluno respondeu" de "o cartao esta como saiu de fabrica".
# Tem de ser a marca NO IDIOMA EM QUE O CARTAO NASCEU — se o cartao sai em ingles
# e a marca e procurada em portugues, todo cartao vazio parece respondido, e o
# passo processa o texto-guia como se fosse a resposta da IA.
_MARCA_GUIA = T("Cole AQUI a resposta da sua IA")


# ─────────────────────────────────────────────────────────────────────────────
# O cartao
# ─────────────────────────────────────────────────────────────────────────────
# Os prefixos que identificam o cartao da Caixa. O do IDIOMA ATUAL vem primeiro;
# os outros ficam para reconhecer um quadro que nasceu antes (ou noutra lingua) —
# a alternativa e criar um segundo cartao a cada rodada e nunca limpar o primeiro.
_PREFIXOS_CAIXA = tuple(dict.fromkeys(
    p.strip().lower() for p in (CARTAO, "0. caixa de entrada", "0. inbox")))


def _e_a_caixa(card):
    titulo = (card.get("titulo") or "").strip().lower()
    return titulo.startswith(_PREFIXOS_CAIXA)


def _lista_id(listas=None):
    """O id da Referencia — com ou sem acento, criando-a se o quadro ainda nao tem."""
    listas = listas if listas is not None else list_lists()
    for nome in LISTA_ALTERNATIVAS:
        lid = find_list_id_by_name(nome, listas)
        if lid:
            return lid
    return create_list(LISTA)


def primeiro(lid, card_id):
    """Poe o cartao na FRENTE da lista. Conforto, nunca obstaculo: se a API recusar
    a reordenacao, o cartao continua onde esta' e o passo segue."""
    try:
        ids = [c["id"] for c in list_cards(lid)]
        nova = [card_id] + [i for i in ids if i != card_id]
        if nova != ids:
            ordenar_cartoes(lid, nova)
    except Exception:
        pass


def _aposentar_lista_antiga(log=None):
    """Apaga a lista "Caixa de entrada" — mas SO' se ela estiver vazia.

    Ela existia para hospedar um cartao so'. Depois que ele muda para a Referencia
    a lista fica sobrando no fim do quadro. Se o aluno tiver posto qualquer outro
    cartao la' dentro, a lista FICA: apagar levaria o que e' dele junto."""
    lid = find_list_id_by_name(LISTA_ANTIGA)
    if not lid:
        return
    if list_cards(lid):
        if log:
            log(Tf('A lista "{0}" tem outros cartoes seus — deixei como esta.', LISTA_ANTIGA))
        return
    if delete_list(lid) and log:
        log(Tf('Lista "{0}" aposentada (o cartao agora vive na Referencia).', LISTA_ANTIGA))


def garantir(log=None):
    """Devolve o cartao da Caixa de entrada — o PRIMEIRO da lista Referencia.

    Tres caminhos, todos idempotentes:
      ja' esta' na Referencia  -> devolve como esta' (nenhuma escrita)
      esta' em outra lista     -> MOVE (preserva o que o aluno colou) e poe na frente
      nao existe               -> cria com o texto-guia e poe na frente
    Em qualquer caso onde houve mudanca, a lista "Caixa de entrada" e' aposentada
    se tiver ficado vazia."""
    listas = list_lists()
    lid = _lista_id(listas)

    achado = achado_lista = None
    for l in listas:
        for c in list_cards(l["id"]):
            if _e_a_caixa(c):
                achado, achado_lista = c, l["id"]
                break
        if achado:
            break

    if achado and str(achado_lista) == str(lid):
        return achado

    if achado:                                   # veio da lista antiga (ou de outra)
        move_card(achado["id"], lid)
        primeiro(lid, achado["id"])
        if log:
            log(Tf('Cartao "{0}" agora e o primeiro da lista "{1}".', CARTAO, LISTA))
        _aposentar_lista_antiga(log)
        return achado

    # create_card devolve o ID, nao o cartao — quem chama `garantir` espera um dicionario
    # (usa c["id"] e c.get("descricao")). Montamos o cartao aqui em vez de ir busca-lo:
    # acabamos de escreve-lo, sabemos exatamente o que ele tem.
    cid = create_card(lid, CARTAO, _GUIA)
    primeiro(lid, cid)
    if log:
        log(Tf('Cartao "{0}" criado no topo da lista "{1}".', CARTAO, LISTA))
    _aposentar_lista_antiga(log)
    return {"id": cid, "titulo": CARTAO, "descricao": _GUIA}


def texto():
    """O que esta escrito no cartao, em texto limpo ('' se ainda for o texto-guia)."""
    c = garantir()
    t = entradas.texto_do_cartao(c.get("descricao") or "")
    return "" if (not t or _MARCA_GUIA in t) else t


def consumir(log=step):
    """Devolve o cartao ao texto de fabrica — o passo ja' levou a resposta ao quadro.

    Sem isto, rodar o passo seguinte leria a resposta ANTERIOR e o aluno veria o
    curso "responder sozinho" com o conteudo do passo passado.
    """
    c = garantir()
    update_card(c["id"], descricao=_GUIA)
    if log:
        log(T("Caixa de entrada limpa — pronta para a proxima resposta."))


# ─────────────────────────────────────────────────────────────────────────────
# O prompt
# ─────────────────────────────────────────────────────────────────────────────
def entregar(titulo, prompt, extra=None):
    """Entrega o prompt ao aluno (area de transferencia + tela) e ENCERRA o passo.

    Encerrar aqui e' de proposito: o passo so' tem o que fazer depois que a resposta
    estiver no quadro. Quem chama nunca precisa lembrar de dar `return`.
    """
    garantir(log=step)
    copiado = copiar(prompt)
    print()
    print("=" * 72)
    print(f"  PROMPT — {titulo}")
    print("=" * 72)
    print(prompt)
    print("=" * 72)
    if copiado:
        print(T("  [OK] O prompt esta na sua area de transferencia (Ctrl+V para colar)."))
    else:
        print(T("  Copie o texto acima (nao consegui usar a area de transferencia)."))
    print(Tf("  1. Cole na sua IA          2. Copie a resposta INTEIRA"))
    print(Tf('  3. Cole no cartao "{0}" do seu quadro', CARTAO))
    print(Tf("  4. Rode este mesmo passo de novo — ele le o quadro e segue."))
    if extra:
        print()
        print("  " + extra)
    print()
    raise SystemExit(0)


def ler(rotulos, titulo, prompt, obrigatorias=None, extra=None):
    """As secoes pedidas, lidas do cartao. Sem resposta no cartao -> entrega o prompt.

    `rotulos`      os cabecalhos que o prompt mandou a IA escrever (ex.: ["HISTORIAS"])
    `obrigatorias` as que, faltando, nao deixam o passo seguir (padrao: todas)
    """
    bruto = texto()
    if not bruto:
        entregar(titulo, prompt, extra)
    secoes = entradas.secoes(bruto, rotulos)
    # Uma resposta com um unico bloco costuma vir SEM cabecalho — a IA responde
    # direto. Nesse caso o texto todo vale como aquele bloco, em vez de o passo
    # dizer "nao encontrei" diante de uma resposta perfeitamente boa.
    if len(rotulos) == 1:
        chave = entradas._sem_acento(rotulos[0])
        if not secoes.get(chave) and secoes.get("_"):
            secoes[chave] = secoes["_"]
    faltando = [r for r in (obrigatorias if obrigatorias is not None else rotulos)
                if not secoes.get(entradas._sem_acento(r), "").strip()]
    if faltando:
        warn(Tf("A resposta no cartao nao traz: {0}", ", ".join(faltando)))
        print(T("  Peca a sua IA para responder com esses cabecalhos e cole de novo.\n"))
        entregar(titulo, prompt, extra)
    secoes["_bruto"] = bruto
    return secoes


def secao(secoes, rotulo):
    """O texto de uma secao pelo rotulo (sem se importar com acento/caixa)."""
    return secoes.get(entradas._sem_acento(rotulo), "").strip()


def valor(secoes, rotulo):
    """O valor de um rotulo escrito NA MESMA LINHA ("META DA SPRINT: <frase>").

    Existe porque `entradas.secoes` so' aceita como cabecalho uma linha com menos
    de 60 caracteres — e uma meta bem escrita passa disso com folga (medido em
    30/08/2026: a resposta estava certa e o passo dizia que faltava). Aqui o
    rotulo e' procurado em qualquer linha, de qualquer tamanho; se ele estiver
    sozinho na linha, vale a proxima linha nao vazia.
    """
    alvo = entradas._sem_acento(rotulo)
    linhas = (secoes.get("_bruto") or "").splitlines()
    for n, linha in enumerate(linhas):
        if ":" not in linha:
            continue
        chave, resto = linha.split(":", 1)
        if entradas._sem_acento(chave) != alvo:
            continue
        resto = resto.strip().strip('"\u201c\u201d').strip()
        if resto:
            return resto
        for seguinte in linhas[n + 1:]:
            if seguinte.strip():
                return seguinte.strip()
    return ""
