/**
 * chatbot.service.ts — Motor de IA Multi-Agente com Gemini Flash.
 *
 * Padrão Cérebro (instrucoes.md §5):
 *  - Não envia histórico completo para economizar tokens
 *  - Mantém resumo de 2 frases por chat (botSummary no banco)
 *  - Micro-prompt de intenção retorna 1 token: "1" = resolver, "2" = escalar
 */
import { GoogleGenerativeAI, type GenerativeModel } from '@google/generative-ai'
import type { WASocket } from '../whatsapp/engine'
import { prisma } from '../../infra/database/prisma'
import { logger } from '../../config/logger'
import { botRepo, chatBotState } from './chatbot.repository'
import type { Server as SocketIOServer } from 'socket.io'

// Cache de clientes Gemini por apiKey (evita recriar para cada mensagem)
const geminiClients = new Map<string, GoogleGenerativeAI>()

function getGeminiClient(apiKey: string): GoogleGenerativeAI {
  if (!geminiClients.has(apiKey)) {
    geminiClients.set(apiKey, new GoogleGenerativeAI(apiKey))
  }
  return geminiClients.get(apiKey)!
}

function getModel(apiKey: string, modelName: string): GenerativeModel {
  return getGeminiClient(apiKey).getGenerativeModel({ model: modelName })
}

// ─── Micro-prompt: classificação de intenção (1 token) ───────────────────────

async function classifyIntent(
  userMsg: string,
  summary: string | null,
  systemPrompt: string,
  model: GenerativeModel
): Promise<'resolve' | 'escalate'> {
  const prompt = [
    `Você é um classificador de intenção para um chatbot de atendimento.`,
    `Prompt do atendente: ${systemPrompt}`,
    summary ? `Contexto da conversa até agora: ${summary}` : '',
    `Nova mensagem do cliente: "${userMsg}"`,
    `Responda APENAS com o número:`,
    `1 = Consigo responder essa pergunta dentro do meu papel`,
    `2 = Precisa de um agente humano`,
  ].filter(Boolean).join('\n')

  try {
    const result = await model.generateContent({
      contents: [{ role: 'user', parts: [{ text: prompt }] }],
      generationConfig: { maxOutputTokens: 2, temperature: 0 },
    })
    const text = result.response.text().trim()
    return text.startsWith('2') ? 'escalate' : 'resolve'
  } catch (err) {
    logger.error({ err }, '[Chatbot] Erro na classificação de intenção — assumindo resolve')
    return 'resolve'
  }
}

// ─── Geração de resposta ──────────────────────────────────────────────────────

async function generateResponse(
  userMsg: string,
  summary: string | null,
  systemPrompt: string,
  model: GenerativeModel
): Promise<string> {
  const prompt = [
    systemPrompt,
    summary ? `\nContexto da conversa até agora:\n${summary}` : '',
    `\nMensagem do cliente: "${userMsg}"`,
    `\nResponda de forma natural, breve e direta. Não use markdown.`,
  ].filter(Boolean).join('\n')

  const result = await model.generateContent({
    contents: [{ role: 'user', parts: [{ text: prompt }] }],
    generationConfig: { maxOutputTokens: 300, temperature: 0.7 },
  })
  return result.response.text().trim()
}

// ─── Atualização do Cérebro (resumo de 2 frases) ─────────────────────────────

async function updateSummary(
  currentSummary: string | null,
  userMsg: string,
  botMsg: string,
  model: GenerativeModel
): Promise<string> {
  const prompt = [
    currentSummary
      ? `Resumo atual da conversa: ${currentSummary}`
      : 'Esta é a primeira troca da conversa.',
    `Nova troca:`,
    `Cliente: "${userMsg}"`,
    `Assistente: "${botMsg}"`,
    `Atualize o resumo em MÁXIMO 2 frases curtas, capturando o essencial da conversa inteira.`,
    `Responda apenas com o resumo, sem introdução.`,
  ].join('\n')

  try {
    const result = await model.generateContent({
      contents: [{ role: 'user', parts: [{ text: prompt }] }],
      generationConfig: { maxOutputTokens: 80, temperature: 0.3 },
    })
    return result.response.text().trim()
  } catch {
    // Em caso de erro, mantém o resumo anterior
    return currentSummary ?? ''
  }
}

// ─── Interface pública ────────────────────────────────────────────────────────

export class ChatbotService {
  constructor(private io: SocketIOServer) {}

  /**
   * Ponto de entrada para cada mensagem recebida.
   * Chamado pelo MessageHandler após persistir a mensagem.
   */
  async handleIncoming(opts: {
    tenantId: string
    instanceId: string
    chatId: string
    jid: string
    text: string
    sock: WASocket
  }): Promise<void> {
    const { tenantId, instanceId, chatId, jid, text, sock } = opts

    // 1. Verifica se há bot ativo para esta instância
    const bot = await botRepo.findEnabled(tenantId, instanceId)
    if (!bot) return

    // 2. Verifica o estado do chat (human takeover)
    const chatState = await chatBotState.getState(chatId)
    if (!chatState || chatState.botPaused) return

    // 3. Aplica modo de gatilho
    if (bot.triggerMode === 'KEYWORD') {
      const lowerText = text.toLowerCase()
      const hasKeyword = bot.keywords.some((kw) => lowerText.includes(kw.toLowerCase()))
      if (!hasKeyword) return
    }

    try {
      const model = getModel(bot.credential.apiKey, bot.model)
      const summary = chatState.botSummary ?? null

      // 4. Micro-prompt: intenção
      const intent = await classifyIntent(text, summary, bot.systemPrompt, model)

      if (intent === 'escalate') {
        // Pausa o bot e notifica via Socket.IO
        await chatBotState.pauseBot(chatId)
        this.io.to(`chat:${chatId}`).emit('bot:escalated', {
          chatId,
          message: 'Bot pausado — atendente humano necessário',
        })
        logger.info({ chatId, jid }, '[Chatbot] Escalação para humano')
        return
      }

      // 5. Gera resposta
      const responseText = await generateResponse(text, summary, bot.systemPrompt, model)

      // 6. Envia via Baileys
      const sent = await sock.sendMessage(jid, { text: responseText })

      // 7. Persiste mensagem do bot no banco
      const chat = await prisma.chat.findUnique({ where: { id: chatId } })
      if (chat) {
        const botMsg = await prisma.message.create({
          data: {
            tenantId,
            instanceId,
            chatId,
            remoteJid: jid,
            messageId: sent?.key.id ?? `bot-${Date.now()}`,
            fromMe: true,
            type: 'TEXT',
            body: responseText,
            status: 'SENT',
            timestamp: new Date(),
          },
        })

        // Notifica frontend
        this.io.to(`chat:${chatId}`).emit('message:new', botMsg)
      }

      // 8. Atualiza Cérebro
      const newSummary = await updateSummary(summary, text, responseText, model)
      await chatBotState.updateSummary(chatId, newSummary)

      logger.info({ chatId, jid, intent }, '[Chatbot] Resposta enviada')
    } catch (err) {
      logger.error({ err, chatId }, '[Chatbot] Erro ao processar mensagem')
    }
  }
}
