"""Comandos em linguagem natural no grupo Agente, interpretados pelo Gemini.

O modelo so escolhe entre operacoes fixas sobre os itens do agente; acoes que apagam ou mexem em
varios itens de uma vez ficam pendentes ate o usuario confirmar."""
import time
import unicodedata
import uuid
from datetime import datetime

from . import agenda_sync, auditoria, llm, preferencias, whatsapp
from .banco import agora, atualizar_item, gravar_item, itens_abertos, mensagens, progresso, salvar_progresso
from .config import FUSO, MODELO_EXTRACAO

NULO = {"type": ["string", "null"]}
ESQUEMA = {
    "type": "object",
    "properties": {
        "acoes": {"type": "array", "items": {"type": "object", "properties": {
            "op": {"type": "string", "enum": ["adicionar", "alterar", "concluir", "remover", "reabrir",
                                              "lembrar_preferencia", "esquecer_preferencia", "explicar"]},
            "item_id": {"type": ["integer", "null"], "description": "id do item, ou numero da preferencia a esquecer"},
            "preferencia": {"type": ["string", "null"], "description": "regra permanente, escrita de forma autocontida"},
            "tipo": {"type": ["string", "null"], "description": "compromisso, tarefa ou projeto"},
            "titulo": NULO, "descricao": NULO, "inicio": NULO, "fim": NULO, "prazo": NULO, "projeto": NULO,
        }, "required": ["op"]}},
        "resposta": {"type": "string"},
        "pedir_confirmacao": {"type": "boolean"},
    },
    "required": ["acoes", "resposta", "pedir_confirmacao"],
}

SISTEMA = """Voce e o assistente de agenda do Joao e recebe mensagens dele no grupo "Agente" do WhatsApp. \
Interprete o pedido e devolva as operacoes sobre os itens dele:
- adicionar: novo item (tipo compromisso/tarefa/projeto; compromisso usa inicio/fim AAAA-MM-DDTHH:MM, tarefa usa prazo).
- alterar: muda campos de um item existente (item_id). "Adiar" e alterar a data.
- concluir: marca como feito. remover: descarta (nao era tarefa, foi cancelado). reabrir: volta a ficar aberto.
- lembrar_preferencia: regra que vale daqui para frente ("nunca mostre X", "nao sou representante da T2", \
"relatorios de FIS-46 quem entrega e meu colega"). Escreva em "preferencia" uma frase autocontida. \
esquecer_preferencia: remove a preferencia de numero item_id.
- explicar: o Joao quer saber de onde veio um item ("de onde veio o #56?", "por que isso ta aqui?"). Use item_id; \
o trecho da conversa e anexado automaticamente, entao a "resposta" pode ser so uma frase curta.
- Uma mensagem pode ter varios pedidos: trate TODOS (ex.: concluir dois itens e ainda guardar uma preferencia).
- Perguntas ("o que tenho quinta?", "quais as entregas da semana?") nao geram acoes: responda em "resposta".
Resolva datas relativas a partir de agora. Identifique itens pelo numero (#12) ou pela descricao.
Se o pedido for ambiguo, nao faca acoes e pergunte em "resposta".
pedir_confirmacao = true quando a mensagem pedir remover algo, ou mexer em mais de 3 itens de uma vez.
"resposta": curta, em portugues, dizendo o que foi (ou sera, se pedir confirmacao) feito, citando os #ids. \
Formatacao do WhatsApp (*negrito*)."""

explicacoes: list[str] = []  # contexto dos itens pedidos com "explicar", anexado a resposta

SIM = {"sim", "s", "confirmo", "pode", "ok", "isso", "manda", "confirma"}
NAO = {"nao", "n", "cancela", "cancelar", "deixa", "esquece"}


def _norm(t):
    return unicodedata.normalize("NFKD", t.lower()).encode("ascii", "ignore").decode().strip(" .!")


def _executar(con, acoes):
    feitos = []
    explicacoes.clear()
    nova_pref = False
    for a in acoes:
        op, iid = a["op"], a.get("item_id")
        campos = {k: a[k] for k in ("titulo", "descricao", "inicio", "fim", "prazo", "projeto", "tipo") if a.get(k)}
        if op == "lembrar_preferencia" and a.get("preferencia"):
            preferencias.adicionar(con, a["preferencia"])
            feitos.append("preferencia guardada")
            nova_pref = True
            continue
        if op == "esquecer_preferencia":
            if iid and preferencias.remover(con, iid):
                feitos.append(f"preferencia {iid} esquecida")
            continue
        if op == "explicar":
            item = iid and con.execute("SELECT * FROM itens WHERE id = ?", (iid,)).fetchone()
            if item:
                explicacoes.append(auditoria.texto_contexto(con, item))
            continue
        if op == "adicionar" and a.get("titulo"):
            iid = gravar_item(con, {**campos, "tipo": a.get("tipo") or "tarefa", "status": "aberto",
                                    "confianca": 1.0, "origem": "manual", "origem_ref": uuid.uuid4().hex,
                                    "evidencia": "adicionado pelo grupo Agente"})
        elif iid and con.execute("SELECT 1 FROM itens WHERE id = ?", (iid,)).fetchone():
            if op == "alterar":
                atualizar_item(con, iid, **campos)
            else:
                atualizar_item(con, iid, status={"concluir": "concluido", "remover": "descartado",
                                                 "reabrir": "aberto"}[op])
        else:
            continue
        feitos.append(f"{op} #{iid}")
    if nova_pref:
        n = preferencias.aplicar(con, log=lambda *_: None)
        if n:
            feitos.append(f"{n} itens tirados pela preferencia")
    if feitos:
        agenda_sync.sincronizar(con, log=lambda *_: None)
        auditoria.gerar(con)
    return feitos


def _itens_texto(con):
    linhas = []
    for i in itens_abertos(con):
        quando = i["inicio"] or i["prazo"] or "sem data"
        conf = " (a confirmar)" if i["status"] == "a_confirmar" else ""
        linhas.append(f"#{i['id']} [{i['tipo']}] {i['titulo']} — {quando}{conf} [{i['origem']}]")
    return "\n".join(linhas) or "(nenhum)"


def processar(con, log=print):
    grupo = whatsapp.GRUPO_AGENTE
    ultimo = progresso(con, "cmd:ultimo_ts")
    if ultimo is None:  # primeira execucao: ignora o que ja estava no grupo
        salvar_progresso(con, "cmd:ultimo_ts", int(time.time()))
        return
    enviadas = set(progresso(con, "wa:enviadas", []))
    linhas = [dict(r) for r in mensagens().execute(
        "SELECT chat, id, ts, tipo, texto, mime, midia, nome_arquivo FROM mensagens "
        "WHERE chat = ? AND ts > ? AND de_mim = 1 ORDER BY ts", (grupo, ultimo)) if r["id"] not in enviadas]
    if not linhas:
        return
    if any(r["tipo"] == "audioMessage" and not r["midia"] for r in linhas) and time.time() - linhas[-1]["ts"] < 120:
        return  # audio ainda baixando: tenta no proximo minuto
    salvar_progresso(con, "cmd:ultimo_ts", linhas[-1]["ts"])
    partes = []
    for r in linhas:
        if r["tipo"] == "audioMessage" and r["midia"]:
            partes.append(whatsapp._processar_midia(r).removeprefix("[audio] "))  # audio vira texto
        elif r["texto"]:
            partes.append(r["texto"])
    if not partes:
        return
    texto = "\n".join(partes)
    ouvido = (f"🎙️ _Entendi: \"{texto[:2000]}\"_\n\n"
              if any(r["tipo"] == "audioMessage" for r in linhas) else "")
    pendente = progresso(con, "cmd:pendente")

    if pendente and _norm(texto) in SIM:
        feitos = _executar(con, pendente["acoes"])
        salvar_progresso(con, "cmd:pendente", None)
        whatsapp.enviar(f"✅ Feito: {', '.join(feitos) or 'nada a alterar'}.", con)
        return
    if pendente and _norm(texto) in NAO:
        salvar_progresso(con, "cmd:pendente", None)
        whatsapp.enviar("👍 Cancelado, nada foi alterado.", con)
        return

    prefs = "\n".join(f"{n}. {p['texto']}" for n, p in enumerate(preferencias.listar(con), 1)) or "(nenhuma)"
    entrada = (f"Agora: {datetime.now(FUSO):%A %d/%m/%Y %H:%M}\n\nItens abertos:\n{_itens_texto(con)}\n\n"
               f"Preferencias guardadas:\n{prefs}\n\nMensagem do Joao:\n{texto}")
    r = llm.gerar(MODELO_EXTRACAO, entrada, sistema=SISTEMA, esquema=ESQUEMA, con=con, etapa="comando")
    acoes = r.get("acoes", [])
    precisa = r.get("pedir_confirmacao") or len(acoes) > 3 or any(a["op"] == "remover" for a in acoes)
    if acoes and precisa:
        salvar_progresso(con, "cmd:pendente", {"acoes": acoes, "em": agora()})
        whatsapp.enviar(f"{ouvido}{r['resposta']}\n\n_Confirma? Responda *sim* ou *não*._", con)
        return
    feitos = _executar(con, acoes)
    log(f"comando: {feitos}")
    extra = "".join(f"\n\n{x}" for x in explicacoes)
    whatsapp.enviar(ouvido + r["resposta"] + extra, con)
