"""Selecao das conversas relevantes, texto das midias e envio para o grupo Agente."""
import io
import json
import time
import urllib.request
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime

from . import llm
from .banco import mensagens
from .config import (API_WHATSAPP, DADOS, DIAS_PROJETOS, DIR_MIDIA, FUSO, MAX_MIDIA_MB, MODELO_MIDIA,
                     SEGREDOS)

GRUPO_AGENTE = json.loads((SEGREDOS / "whatsapp.json").read_text())["grupoAgente"]


def meus_ids() -> list[str]:
    """Numero e LID do usuario, para achar mencoes em grupos."""
    creds = json.loads((DADOS / "whatsapp-auth/creds.json").read_text())
    me = creds.get("me", {})
    return [x.split(":")[0].split("@")[0] for x in (me.get("id"), me.get("lid")) if x]


def chats_relevantes(desde_ts: int) -> list[str]:
    """Conversas diretas + grupos onde o usuario falou + grupos onde foi mencionado."""
    con = mensagens()
    diretas = [r[0] for r in con.execute(
        "SELECT DISTINCT chat FROM mensagens WHERE ts > ? AND chat NOT LIKE '%@g.us'", (desde_ts,))]
    grupos = [r[0] for r in con.execute(
        "SELECT chat FROM mensagens WHERE ts > ? AND chat LIKE '%@g.us' GROUP BY chat HAVING SUM(de_mim) > 0",
        (desde_ts,))]
    filtro = " OR ".join(["texto LIKE ?"] * (len(meus_ids()) + 1))
    mencoes = [r[0] for r in con.execute(
        f"SELECT DISTINCT chat FROM mensagens WHERE ts > ? AND chat LIKE '%@g.us' AND ({filtro})",
        (desde_ts, *[f"%@{i}%" for i in meus_ids()], "%Penna%"))]
    return [c for c in dict.fromkeys(diretas + grupos + mencoes) if c != GRUPO_AGENTE]


# ---------- midias ----------
PROMPT_AUDIO = "Transcreva fielmente este audio em portugues. Responda apenas com a transcricao."
PROMPT_DOC = ("Extraia deste documento o que importa para a agenda de quem o recebeu: resumo em ate 12 linhas, "
              "incluindo TODAS as datas, horarios, prazos, tarefas, responsaveis e valores mencionados.")
PROMPT_IMG = ("Descreva esta imagem em 1 a 3 linhas. Se houver texto (print de conversa, aviso, cronograma, "
              "comprovante, convite), transcreva datas, horarios, prazos e tarefas. Se for meme, figurinha ou "
              "foto sem informacao util, responda apenas: [sem informacao]")


def _texto_documento(caminho, mime, nome):
    ext = caminho.suffix.lower()
    dados = caminho.read_bytes()
    if ext == ".pdf" or mime == "application/pdf":
        return [llm.parte_bytes(dados, "application/pdf"), PROMPT_DOC]
    if ext == ".docx":
        import docx
        texto = "\n".join(p.text for p in docx.Document(io.BytesIO(dados)).paragraphs)
    elif ext in (".xlsx", ".xlsm"):
        import openpyxl
        wb = openpyxl.load_workbook(io.BytesIO(dados), read_only=True, data_only=True)
        linhas = []
        for ws in wb.worksheets[:5]:
            for row in ws.iter_rows(max_row=200, values_only=True):
                if any(v is not None for v in row):
                    linhas.append(" | ".join("" if v is None else str(v) for v in row))
        texto = "\n".join(linhas)
    elif ext in (".txt", ".csv", ".md"):
        texto = dados.decode("utf-8", "replace")
    else:
        return None
    return [f"Documento '{nome}':\n{texto[:60000]}", PROMPT_DOC]


def _processar_midia(m):
    caminho = DIR_MIDIA / m["midia"]
    if not caminho.exists() or caminho.stat().st_size > MAX_MIDIA_MB * 1024 * 1024:
        return None
    if m["tipo"] == "audioMessage":
        conteudo = [llm.parte_bytes(caminho.read_bytes(), (m["mime"] or "audio/ogg").split(";")[0]), PROMPT_AUDIO]
        rotulo = "audio"
    elif m["tipo"] == "imageMessage":
        conteudo = [llm.parte_bytes(caminho.read_bytes(), m["mime"] or "image/jpeg"), PROMPT_IMG]
        rotulo = "imagem"
    else:
        conteudo = _texto_documento(caminho, m["mime"], m["nome_arquivo"] or caminho.name)
        rotulo = f"documento {m['nome_arquivo'] or ''}".strip()
        if conteudo is None:
            return f"[{rotulo}: formato nao lido]"
    texto = llm.gerar(MODELO_MIDIA, conteudo, max_saida=2048, etapa="midia").strip()
    return f"[{rotulo}] {texto}"


def processar_midias(con_agente, chats, desde_ts, paralelo=4):
    """Transcreve/le as midias ainda sem texto. Devolve quantas processou."""
    con = mensagens()
    marcas = ",".join("?" * len(chats))
    pendentes = [dict(r) for r in con.execute(
        f"""SELECT chat, id, tipo, mime, midia, nome_arquivo FROM mensagens
            WHERE midia IS NOT NULL AND ts > ? AND chat IN ({marcas})
              AND tipo IN ('audioMessage', 'imageMessage', 'documentMessage') ORDER BY ts""",
        (desde_ts, *chats))]
    feitos = {(r["chat"], r["id"]) for r in con_agente.execute("SELECT chat, id FROM midia_texto")}
    pendentes = [m for m in pendentes if (m["chat"], m["id"]) not in feitos]

    def tarefa(m):
        try:
            return m, _processar_midia(m)
        except Exception as e:  # uma midia ruim nao para o lote
            return m, f"[{m['tipo']}: erro ao ler ({type(e).__name__})]"

    n = 0
    with ThreadPoolExecutor(paralelo) as ex:
        for m, texto in ex.map(tarefa, pendentes):
            if texto:
                con_agente.execute("INSERT OR REPLACE INTO midia_texto VALUES (?, ?, ?)", (m["chat"], m["id"], texto))
                n += 1
                if n % 25 == 0:
                    con_agente.commit()
    con_agente.commit()
    return n


# ---------- transcricao das conversas ----------
def linhas_conversa(con_agente, chat, desde_ts, ate_ts=None):
    """Mensagens de um chat como dicts com o texto final (incluindo midias transcritas)."""
    con = mensagens()
    q = "SELECT * FROM v_mensagens WHERE chat = ? AND ts > ?" + (" AND ts <= ?" if ate_ts else "") + " ORDER BY ts"
    linhas = []
    for r in con.execute(q, (chat, desde_ts, ate_ts) if ate_ts else (chat, desde_ts)):
        texto = r["texto"]
        if r["tipo"] in ("audioMessage", "imageMessage", "documentMessage"):
            mt = con_agente.execute("SELECT texto FROM midia_texto WHERE chat = ? AND id = ?", (chat, r["id"])).fetchone()
            midia = mt["texto"] if mt else f"[{r['tipo'].replace('Message', '')} nao lido]"
            if "[sem informacao]" in midia and not texto:
                continue
            texto = f"{midia}" + (f" | legenda: {texto}" if texto else "")
        elif r["tipo"] in ("stickerMessage",):
            continue
        if not texto:
            continue
        quando = datetime.fromtimestamp(r["ts"], FUSO).strftime("%a %d/%m %H:%M")
        autor = r["autor_nome"] or ("alguem" if r["em_grupo"] else (r["chat_nome"] or "contato"))
        if r["citada"]:
            texto = f"(respondendo a: \"{r['citada'][:120]}\") {texto}"
        linhas.append({"id": r["id"], "ts": r["ts"], "texto": f"[{quando}] {autor}: {texto}",
                       "chat_nome": r["chat_nome"], "em_grupo": r["em_grupo"]})
    return linhas


# ---------- envio ----------
def enviar(texto: str, con=None):
    """Envia ao grupo Agente. Com `con`, registra o id para os comandos nao lerem a propria mensagem."""
    texto = texto.replace("**", "*")  # o modelo as vezes usa markdown; o WhatsApp usa *negrito*
    req = urllib.request.Request(f"{API_WHATSAPP}/enviar", data=json.dumps({"texto": texto}).encode(),
                                 headers={"Content-Type": "application/json"}, method="POST")
    for tentativa in range(3):
        try:
            r = json.load(urllib.request.urlopen(req, timeout=30))
            break
        except Exception:
            if tentativa == 2:
                raise
            time.sleep(10)
    if con is not None and r.get("id"):
        from .banco import progresso, salvar_progresso
        salvar_progresso(con, "wa:enviadas", (progresso(con, "wa:enviadas", []) + [r["id"]])[-300:])
    return r


def desde(dias: int) -> int:
    return int(time.time()) - dias * 86400


__all__ = ["chats_relevantes", "processar_midias", "linhas_conversa", "enviar", "desde", "DIAS_PROJETOS"]
