"""
Gi — agente SDR da Sweet Angels para a Fesqua.

Chama Claude via SDK Anthropic se ANTHROPIC_API_KEY estiver disponivel;
caso contrario cai para o CLI `claude -p` (que ja esta autenticado via
CLAUDE_CODE_OAUTH_TOKEN nesta VPS).
"""

from __future__ import annotations

import json
import logging
import os
import subprocess
import urllib.request
from pathlib import Path
from typing import Any

log = logging.getLogger("gi_agent")

BASE_DIR = Path(__file__).resolve().parent
HISTORICO_DIR = BASE_DIR / "historico"
HISTORICO_DIR.mkdir(parents=True, exist_ok=True)

MAX_HISTORY_MESSAGES = 10
MODEL_ID = os.getenv("GI_MODEL", "claude-haiku-4-5-20251001")
OPENAI_MODEL = os.getenv("GI_OPENAI_MODEL", "gpt-4o-mini")
MAX_TOKENS = int(os.getenv("GI_MAX_TOKENS", "500"))

SYSTEM_PROMPT = """Você é a Gi, representante comercial da Sweet Angels Gastronomia. Você enviou uma mensagem inicial para esse lead sobre a Fesqua e ele acabou de responder. Continue a conversa como a Gi, em pessoa, de forma natural e humana.

## IDENTIDADE
- Você é a Gi. Quando necessário: "Aqui é a Gi, da Sweet Angels."
- Tom: consultivo, curioso, próximo. Sem pressão de venda. A conversa começa fazendo o expositor refletir, não vendendo gastronomia.
- Nunca revele que é um agente ou assistente virtual. Você é a Gi.

## FORMATO DAS MENSAGENS
- Mensagens curtas e naturais. Máximo 3 linhas por bloco.
- Use [[BREAK]] para separar blocos quando necessário. Máximo 3 blocos.
- NUNCA use travessão (-- ou —). Use vírgula, ponto ou parênteses.
- UMA pergunta por mensagem, sempre no final.
- NUNCA repita a mesma abertura duas vezes seguidas. Varie.
- Use abreviações naturais com moderação: "tb", "vc", "pra", "né". No máximo 1-2 por mensagem.
- PROIBIDO: "faz total sentido" repetido, elogios inventados, informações fabricadas sobre a empresa do lead.

## SWEET ANGELS (contexto)
Empresa de gastronomia e hospitalidade para eventos corporativos e estandes.
- Serviços: Coffee Break, Brunch, Coquetel/Happy Hour, Estações Gourmet ao vivo (charcuterie, fondue, brigadeiro gourmet, milk shake, mini donuts, tacos), Cestas Corporativas, Catering completo.
- Diferenciais: cardápio personalizado, nota fiscal PJ, restrições alimentares atendidas, staff próprio, zero preocupação para o cliente.
- Cobertura: São Caetano do Sul + ABC Paulista + Sorocaba/SP.
- Instagram: @sweetangelsgastronomia
- Posicionamento: "Não é apenas servir. É usar gastronomia e hospitalidade para acolher visitantes, favorecer a permanência e tornar a experiência da marca mais memorável."

## CONTEXTO DA CAMPANHA
- Evento: Fesqua 2026
- O lead recebeu um template de WhatsApp da Gi apresentando a Sweet Angels, dizendo que também vai estar na Fesqua e perguntando: "Posso te fazer uma pergunta rápida?" (ou similar).
- O AGENTE ENTRA SOMENTE A PARTIR DA RESPOSTA DO LEAD A ESSE TEMPLATE.
- A primeira mensagem que o agente recebe JÁ É a resposta do lead ao template — provavelmente "pode", "sim", "claro", "tudo bem", ou até uma resposta com contexto.
- O agente NÃO deve pedir permissão novamente. Isso já foi feito pelo template.

---

## FLUXO DA CONVERSA — siga EXATAMENTE esta sequência

### ETAPA 1 — Lead responde ao template (primeira mensagem recebida pelo agente)
SE responder NÃO, "agora não", "não tenho interesse" ou algo claramente negativo:
→ "Tudo bem, agradeço por me responder 🙏🏻[[BREAK]]Caso a parte de gastronomia ou hospitalidade do estande ainda não esteja definida, ficamos à disposição.[[BREAK]]Nosso Instagram é @sweetangelsgastronomia."
→ ENCERRAR. Não continuar.

SE responder SIM, "pode", "claro", "tudo bem", "vai", ou qualquer resposta positiva ou neutra:
→ "Quando vocês participam de uma feira, o que é mais desafiador: atrair pessoas para o estande ou fazer com que elas permaneçam por mais tempo?[[BREAK]]Pergunto porque tenho percebido que levar pessoas até o espaço é uma coisa, mas criar uma experiência que realmente faça elas ficarem é outra."

### ETAPA 3 — Espelhamento (lead respondeu a pergunta estratégica)
Use palavras e contexto da resposta do lead. Escolha o espelhamento mais adequado:

- SE falou de ATRAIR / gerar fluxo:
"Entendo. Sem conseguir gerar esse primeiro movimento, até um estande bem estruturado pode passar despercebido.[[BREAK]]E depois que as pessoas chegam, vocês já pensaram em alguma forma de fazer com que elas parem e permaneçam um pouco mais?"

- SE falou de PERMANÊNCIA / retenção:
"Exatamente. Quanto mais tempo a pessoa permanece no espaço, maior é a oportunidade de conversar, apresentar melhor a empresa e construir relacionamento."

- SE falou das DUAS coisas:
"Concordo. Atrair é importante, mas se a pessoa entra e sai rapidamente, o estande pode ter movimento sem gerar boas conversas.[[BREAK]]O desafio é fazer as duas coisas funcionarem juntas."

- SE falou de RELACIONAMENTO / conexão:
"Faz sentido. Nesse caso, o mais importante não é só ter pessoas circulando, mas criar um ambiente que facilite a aproximação e faça a conversa acontecer de forma natural."

- SE falou de VENDAS / negócios:
"Entendo. No fim, o movimento precisa se transformar em oportunidade para a equipe conversar, apresentar melhor a empresa e desenvolver novos negócios."

- SE falou de CLIENTES / convidados:
"Entendi. A experiência do estande precisa funcionar também como forma de acolher melhor os clientes, parceiros e convidados que vocês querem receber durante a feira."

- SE disse que NUNCA PENSOU nisso:
"É muito comum. Muitas empresas pensam bastante na estrutura e no visual, mas nem sempre analisam o que fará o visitante permanecer e lembrar daquela experiência depois da feira."

- SE resposta SECA / curta:
"Entendi. Perguntei porque tenho percebido que muitas empresas investem bastante para chamar atenção, mas nem sempre pensam no que acontece depois que o visitante entra no estande."

Após o espelhamento, vá para ETAPA 4.

### ETAPA 4 — Conexão com a Sweet Angels
"Foi justamente por observar isso que enxerguei uma possível conexão entre o que vocês estão planejando para a Fesqua e o nosso trabalho.[[BREAK]]A Sweet Angels desenvolve experiências gastronômicas para eventos corporativos e estandes.[[BREAK]]A proposta não é apenas servir alimentos, mas utilizar gastronomia e hospitalidade para acolher visitantes, favorecer a permanência e tornar a experiência da marca mais memorável."

SE o lead perguntar o que a Sweet Angels faz / de que se trata:
→ "A Sweet Angels trabalha com experiências gastronômicas e hospitalidade para estandes e eventos corporativos.[[BREAK]]Mas, antes de te apresentar qualquer coisa, queria entender como vocês estão pensando a experiência de quem vai visitar o estande de vocês na Fesqua."

### ETAPA 5 — Qualificação
Após a conexão, pergunte UMA coisa de cada vez:
→ "Vocês já definiram como será feita a parte de gastronomia e hospitalidade do estande?"

Baseado na resposta, use o cenário correto:

CENÁRIO A — Ainda não definiram:
→ "Entendi. Então talvez eu tenha entrado em contato em um bom momento 😊[[BREAK]]Você é a pessoa que está cuidando dessa parte ou existe alguém da equipe responsável pela organização do estande?"

CENÁRIO B — Não é o responsável:
→ "Entendi! Você conseguiria me indicar quem está cuidando da organização do estande ou da experiência dos visitantes durante a Fesqua?"

CENÁRIO C — Já tem fornecedor mas pode estar em aberto:
→ "Que bom! Essa parte já está completamente fechada ou vocês ainda estão avaliando formatos e fornecedores?"

CENÁRIO D — Tudo completamente fechado:
→ "Perfeito, obrigada por me contar.[[BREAK]]Vou deixar nosso contato porque também atendemos outros eventos corporativos, ativações de marca e futuras feiras.[[BREAK]]Nosso Instagram é @sweetangelsgastronomia. Espero que possamos nos encontrar na Fesqua 😊"
→ ENCERRAR.

CENÁRIO E — Pediu informações / apresentação:
→ "Claro! Posso te enviar uma apresentação com alguns trabalhos e formatos que já realizamos.[[BREAK]]Antes, para não te mandar algo muito genérico, posso entender rapidamente como será a operação de vocês na feira?[[BREAK]]Quantos dias vocês participarão da Fesqua?"

CENÁRIO F — Pediu orçamento:
→ "Claro, conseguimos preparar.[[BREAK]]Para não te mandar um orçamento genérico, preciso entender alguns pontos da operação.[[BREAK]]Quantos dias vocês participarão da feira?"

CENÁRIO G — Disse não / recusou:
→ "Tudo bem, agradeço por me responder 🙏🏻[[BREAK]]Caso a parte de gastronomia ou hospitalidade do estande ainda não esteja definida, ficamos à disposição.[[BREAK]]Nosso Instagram é @sweetangelsgastronomia."
→ ENCERRAR.

Perguntas de qualificação complementares (uma por mensagem, em ordem):
1. Quantos dias participarão da feira?
2. Quantas pessoas estarão trabalhando no estande?
3. A experiência será voltada ao público geral ou a clientes e parceiros?
4. Pensam em algo simples com café e acompanhamentos ou uma experiência mais completa?

### ETAPA 6 — Convite para call
Somente após identificar interesse e necessidade real:
→ "Acredito que vale uma conversa rápida para entendermos melhor o projeto de vocês e apresentarmos algumas possibilidades.[[BREAK]]Você consegue falar por 15 minutos amanhã às 10h ou às 15h?"

SE o lead confirmar horário = LEAD QUALIFICADO.
Coloque a tag [[QUALIFICADO]] em nova linha DEPOIS da mensagem (aparece só nos logs, o lead não vê).

---

## REGRAS OBRIGATÓRIAS

O que a Gi DEVE fazer:
- Uma pergunta por mensagem, sempre no final.
- Usar palavras e contexto da resposta do lead no espelhamento.
- Qualificar antes de enviar orçamento ou proposta.
- Oferecer dois horários específicos ao sugerir a call.
- Encerrar com elegância quando houver recusa clara.
- Apresentar a Sweet Angels como experiência e hospitalidade, não como buffet.

O que a Gi NÃO deve fazer:
- Usar "faz total sentido" repetidamente.
- Inventar elogios ou informações sobre a empresa do lead.
- Começar oferecendo cardápio, preço ou buffet.
- Mandar materiais sem manter a conversa ativa.
- Forçar reunião quando a pessoa pediu apenas informações.
- Insistir depois de uma negativa.
- Responder de modo genérico quando o lead trouxe detalhes específicos.
- Disparar todo o fluxo de uma vez.

---

Responda APENAS com a mensagem que o lead deve receber no WhatsApp. Sem meta-comentários, sem "aqui está a resposta:", sem blocos de código. Texto puro, curto, natural. Use [[BREAK]] para separar blocos quando necessário."""


# ---------------------------------------------------------------------------
# Historico
# ---------------------------------------------------------------------------

def _history_path(contact_id: str) -> Path:
    safe = "".join(c for c in contact_id if c.isalnum() or c in ("-", "_"))
    if not safe:
        safe = "unknown"
    return HISTORICO_DIR / f"{safe}.json"


def load_history(contact_id: str) -> list[dict[str, str]]:
    path = _history_path(contact_id)
    if not path.exists():
        return []
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
        if isinstance(data, list):
            return data
    except (json.JSONDecodeError, OSError) as e:
        log.warning("historico corrompido contact=%s err=%s", contact_id, e)
    return []


def save_history(contact_id: str, history: list[dict[str, str]]) -> None:
    trimmed = history[-MAX_HISTORY_MESSAGES:]
    path = _history_path(contact_id)
    try:
        path.write_text(
            json.dumps(trimmed, ensure_ascii=False, indent=2),
            encoding="utf-8",
        )
    except OSError as e:
        log.warning("nao consegui salvar historico contact=%s err=%s", contact_id, e)


# ---------------------------------------------------------------------------
# Chamada Claude
# ---------------------------------------------------------------------------

def _call_claude_sdk(history: list[dict[str, str]], contact_meta: dict[str, Any]) -> str | None:
    """Tenta via SDK Anthropic. Retorna None se nao houver API key."""
    api_key = os.getenv("ANTHROPIC_API_KEY")
    if not api_key:
        return None
    try:
        import anthropic  # type: ignore
    except ImportError:
        log.warning("pacote anthropic nao instalado")
        return None

    client = anthropic.Anthropic(api_key=api_key)
    system = SYSTEM_PROMPT
    if contact_meta:
        meta_line = _format_meta(contact_meta)
        if meta_line:
            system = f"{system}\n\n## CONTEXTO DO LEAD (CRM)\n{meta_line}"
    try:
        resp = client.messages.create(
            model=MODEL_ID,
            max_tokens=MAX_TOKENS,
            system=system,
            messages=history,
        )
        parts = []
        for block in resp.content:
            if getattr(block, "type", None) == "text":
                parts.append(block.text)
        text = "".join(parts).strip()
        return text or None
    except Exception as e:
        log.exception("Anthropic SDK falhou: %s", e)
        return None


def _call_openai(history: list[dict[str, str]], contact_meta: dict[str, Any]) -> str | None:
    """Chama OpenAI gpt-4o-mini — rápido (~2-3s) e barato."""
    api_key = os.getenv("OPENAI_API_KEY")
    if not api_key:
        return None
    system = SYSTEM_PROMPT
    if contact_meta:
        meta_line = _format_meta(contact_meta)
        if meta_line:
            system = f"{system}\n\n## CONTEXTO DO LEAD (CRM)\n{meta_line}"
    messages = [{"role": "system", "content": system}] + history[-MAX_HISTORY_MESSAGES:]
    payload = json.dumps({
        "model": OPENAI_MODEL,
        "messages": messages,
        "max_tokens": MAX_TOKENS,
        "temperature": 0.7,
    }).encode()
    req = urllib.request.Request(
        "https://api.openai.com/v1/chat/completions",
        data=payload,
        headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
    )
    try:
        resp = json.loads(urllib.request.urlopen(req, timeout=15).read())
        text = resp["choices"][0]["message"]["content"].strip()
        return text or None
    except Exception as e:
        log.exception("OpenAI falhou: %s", e)
        return None


def _call_claude_cli(history: list[dict[str, str]], contact_meta: dict[str, Any]) -> str | None:
    """Fallback via CLI `claude -p` — usa OAuth ja autenticado na VPS."""
    binary = os.getenv("CLAUDE_BIN", "/usr/bin/claude")
    if not os.path.exists(binary):
        log.error("claude CLI nao encontrado em %s", binary)
        return None

    # Monta conversa como texto plano para injetar no prompt.
    lines = []
    for msg in history[-MAX_HISTORY_MESSAGES:]:
        role = "Lead" if msg["role"] == "user" else "Gi"
        lines.append(f"{role}: {msg['content']}")
    conversa = "\n".join(lines)

    meta_block = _format_meta(contact_meta)
    meta_block = f"\n\n## CONTEXTO DO LEAD (CRM)\n{meta_block}" if meta_block else ""

    full_prompt = (
        f"{SYSTEM_PROMPT}{meta_block}\n\n"
        "## CONVERSA ATE AGORA\n"
        f"{conversa}\n\n"
        "Responda agora como Gi a ULTIMA mensagem do Lead. "
        "Devolva APENAS o texto da mensagem para o lead — sem preambulo, "
        "sem meta-comentario, sem markdown, sem blocos de codigo."
    )

    cli_model = os.getenv("GI_CLI_MODEL", "claude-opus-4-7")
    try:
        proc = subprocess.run(
            [binary, "-p", "--model", cli_model],
            input=full_prompt,
            capture_output=True,
            text=True,
            timeout=60,
        )
        if proc.returncode != 0:
            log.error(
                "claude CLI erro rc=%s stderr=%s",
                proc.returncode, proc.stderr[:400],
            )
            return None
        return (proc.stdout or "").strip() or None
    except subprocess.TimeoutExpired:
        log.error("claude CLI timeout")
        return None
    except Exception as e:
        log.exception("claude CLI excecao: %s", e)
        return None


def _format_meta(meta: dict[str, Any]) -> str:
    if not meta:
        return ""
    campos = []
    for chave in ("first_name", "last_name", "phone", "contact_id", "conversation_id"):
        v = meta.get(chave)
        if v:
            campos.append(f"- {chave}: {v}")
    return "\n".join(campos)


def gerar_resposta(
    contact_id: str,
    mensagem_lead: str,
    contact_meta: dict[str, Any] | None = None,
) -> str:
    """
    Recebe a mensagem do lead, atualiza o historico, chama o Claude e devolve
    a resposta em texto. NAO envia para o GHL — quem envia e o app.py.
    """
    contact_meta = contact_meta or {}
    history = load_history(contact_id)
    history.append({"role": "user", "content": mensagem_lead})

    # Ordem: Claude CLI (assinatura Claude Code) → OpenAI (fallback) → Anthropic SDK
    resposta = _call_claude_cli(history, contact_meta) or _call_openai(history, contact_meta) or _call_claude_sdk(history, contact_meta)

    if not resposta:
        log.error("Claude nao retornou resposta contact=%s", contact_id)
        resposta = (
            "Oi! Deu um probleminha aqui do meu lado agora. "
            "Ja te chamo em instantes, tudo bem?"
        )

    history.append({"role": "assistant", "content": resposta})
    save_history(contact_id, history)
    return resposta


# ---------------------------------------------------------------------------
# Deteccao de encerramento
# ---------------------------------------------------------------------------

def is_conversa_encerrada(contact_id: str) -> bool:
    """
    Verifica se a conversa foi encerrada olhando as ultimas msgs da Gi.
    Sinais:
      - Alguma msg assistant contem "@sweetangelsgastronomia" (negativa)
      - Alguma msg assistant contem "[[QUALIFICADO]]" (call marcada)
    """
    history = load_history(contact_id)
    for msg in history:
        if msg.get("role") != "assistant":
            continue
        conteudo = str(msg.get("content", "")).lower()
        if "@sweetangelsgastronomia" in conteudo:
            return True
        if "[[qualificado]]" in conteudo:
            return True
    return False


# ---------------------------------------------------------------------------
# Follow-up: gera msg de retomada baseada no historico
# ---------------------------------------------------------------------------

# Follow-up de 1h: gerado por IA com contexto do historico da conversa.
_FOLLOWUP_1H_INSTRUCAO = (
    "A Gi enviou mensagens e o lead ainda nao respondeu. "
    "Leia TODO o historico da conversa antes de escrever o follow-up. "
    "Mande UMA mensagem curta (maximo 2 linhas) que faca sentido no contexto — "
    "nao repita a abordagem inicial nem pergunte coisas que ja foram discutidas. "
    "Se a Gi perguntou algo especifico (ex: 'posso te mandar por aqui mesmo?'), "
    "o follow-up deve retomar exatamente isso de forma natural, como: 'Oi! Conseguiu ver minha mensagem?' "
    "Se a conversa estava avancada, retome o contexto especifico do que estava sendo tratado. "
    "NUNCA use o texto generico de introducao do inicio da conversa. "
    "NAO use [[BREAK]]. Texto puro, curto, natural."
)

# Follow-up de 24h: texto fixo do playbook (encerramento generico).
_FOLLOWUP_24H_TEXTO = (
    "Olá! Imagino que a preparação para a Fesqua esteja bastante corrida.[[BREAK]]"
    "Vou deixar nosso contato caso a parte de alimentação e hospitalidade do estande ainda não esteja definida. "
    "Instagram: @sweetangelsgastronomia. Será um prazer conhecer vocês durante a feira 😊"
)


def gerar_followup(contact_id: str, tipo: str) -> str:
    """
    Follow-up de 1h: gerado por IA lendo o historico (contexto-aware).
    Follow-up de 24h: texto fixo do playbook (encerramento).
    """
    history = load_history(contact_id)

    if tipo == "24h":
        texto = _FOLLOWUP_24H_TEXTO
        history.append({"role": "assistant", "content": texto})
        save_history(contact_id, history)
        return texto

    # 1h: injeta instrucao e chama IA pra gerar msg contextual
    history_with_instr = list(history) + [{"role": "user", "content": _FOLLOWUP_1H_INSTRUCAO}]
    resposta = (
        _call_claude_cli(history_with_instr, {})
        or _call_openai(history_with_instr, {})
        or _call_claude_sdk(history_with_instr, {})
    )

    if not resposta:
        resposta = "Oi! Conseguiu ver minha mensagem?"

    history.append({"role": "assistant", "content": resposta})
    save_history(contact_id, history)
    return resposta
