'use strict'

/**
 * smart-router.js — Roteador determinístico (sem LLM) para mensagens de alta confiança.
 *
 * Estratégia:
 *   1. Classifica a mensagem do usuário com regex/intents.
 *   2. Para intents "deterministicos" (cotação por ID, horário, endereço, entrega,
 *      pagamento, meus pedidos), busca o dado no DB e responde com template.
 *   3. Bypassa completamente o LLM: 0 tokens, resposta instantânea, 100% precisa.
 *   4. Loga em nw_auto_replies como status='sent_local' para auditoria.
 *
 * Para intents ambíguas ou conversacionais → retorna null e o fluxo segue ao LLM.
 */

const { queryOne, query, execute } = require('../../database/postgres')

// ── Tabela de horário (mantida em sincronia com brain_node "HORÁRIO") ───────
function lojaAberta(date = new Date()) {
  const utc = date.getUTCHours() * 60 + date.getUTCMinutes()
  const minSP = (utc - 180 + 1440) % 1440
  const h = Math.floor(minSP / 60)
  const dow = (date.getUTCDay() + (utc - 180 < 0 ? -1 : 0) + 7) % 7
  let aberto, abreAs, fechaAs
  if (dow === 0)         { aberto = h >= 10 && h < 22; abreAs = '10h'; fechaAs = '22h' }
  else if (dow >= 1 && dow <= 4) { aberto = h >= 8 && h < 23; abreAs = '08h'; fechaAs = '23h' }
  else                   { aberto = h >= 8 || h < 2; abreAs = '08h'; fechaAs = '02h da madrugada' }
  return { aberto, abreAs, fechaAs, h, dow }
}

// ── Fases do pedido (em linguagem natural) ──────────────────────────────────
const FASE_NATURAL = {
  novo:       'recebemos seu pedido e já vamos começar a separar 📦',
  separacao:  'estamos separando os itens no estoque agora 📦',
  embalagem:  'já separamos tudo, estamos embalando 🎁',
  expedicao:  'saiu para entrega! 🚚 Se for retirada, já está pronto pra buscar.',
  entregue:   'foi marcado como *entregue* ✅',
  cancelado:  'foi cancelado',
  auditoria:  'está em conferência interna, libera em alguns minutos',
  fiscal:     'está em processamento fiscal, libera em instantes',
}

const STATUS_FINANCEIRO_TXT = {
  pendente:    'pagamento pendente',
  aprovado:    'pagamento aprovado ✅',
  recusado:    'pagamento recusado ❌',
  reembolsado: 'reembolsado',
}

// ── Detectores de intents determinísticos ───────────────────────────────────

function intentCotacaoOuPedido(msg) {
  // "em que fase está a cotação #1", "status do pedido 3", "PED-20260426-3"
  const proto = msg.match(/PED-\d{8}-\d+/i)
  if (proto) return { tipo: 'protocolo', valor: proto[0].toUpperCase() }
  const cot = msg.match(/cota[çc][aã]o\s*[#nº]?\s*(\d+)/i)
  if (cot) return { tipo: 'cotacao_id', valor: cot[1] }
  const ped = msg.match(/pedido\s*[#nº]?\s*(\d+)/i)
  if (ped) return { tipo: 'pedido_id', valor: ped[1] }
  // "qual o status", "fase do meu pedido" sem número
  if (/\b(status|fase|andamento)\s+(do|da)\s+(meu|minha|ultimo|último)\s+(pedido|cota[çc][aã]o)/i.test(msg)) {
    return { tipo: 'ultimo_do_cliente', valor: null }
  }
  return null
}

function intentHorario(msg) {
  return /\b(abert[oa]s?|fechad[oa]s?|hor[áa]rio|funcion(am|ando|a)|que\s+horas\s+(abre|fecha|fecham))\b/i.test(msg)
    && !/\b(meu|minha)\s+pedido\b/i.test(msg)
}

function intentEndereco(msg) {
  return /\b(endere[çc]o|onde\s+(fica|esta|est[áa]o|voc[êe]s)|localiza[çc][aã]o|como\s+chego|qual\s+(o|a)\s+rua)\b/i.test(msg)
}

function intentEntrega(msg) {
  return /\b(taxa\s+de\s+entrega|frete|entreg(am|a\s+em)|delivery|tele.?entrega|prazo\s+de\s+entrega|valor\s+da\s+entrega|leva\s+at[ée])\b/i.test(msg)
}

function intentPagamento(msg) {
  return /\b(formas?\s+de\s+pagamento|aceitam?\s+(pix|cart[ãa]o|d[ée]bito|cr[ée]dito|dinheiro|boleto)|qual\s+pagamento|posso\s+pagar)\b/i.test(msg)
}

function intentMeusPedidos(msg) {
  return /\b(meus\s+pedidos|minhas\s+compras|hist[óo]rico\s+de\s+(pedidos|compras)|o\s+que\s+(j[áa]\s+)?comprei)\b/i.test(msg)
}

function intentMenu(msg) {
  return /^(menu|op[çc][õo]es|ajuda|help|comandos?)[\s!.\,?]*$/i.test(msg.trim())
}

// ── Templates de resposta ───────────────────────────────────────────────────

function tplHorario() {
  const { aberto, abreAs, fechaAs, dow } = lojaAberta()
  const dia = ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'][dow]
  if (aberto) {
    return `Sim, estamos *abertos* agora! 🟢\nHoje (${dia}) atendemos até ${fechaAs}.\nPosso te ajudar com algum pedido?`
  }
  return `No momento estamos *fechados* 🔴\nHoje (${dia}) abrimos às ${abreAs} e fechamos às ${fechaAs}.\nVocê pode mandar seu pedido por aqui que assim que abrirmos a equipe processa! 😊`
}

function tplEndereco() {
  return `📍 *Alemão Conveniências*\nVocê pode pedir por aqui no WhatsApp ou pelo nosso app: alemaocoonveniencias.com.br\n\nPosso te ajudar a montar um pedido?`
}

function tplEntrega() {
  return `🚚 *Entrega e Retirada*\n\n• *Retirada na loja*: grátis, pronto em até 30 min\n• *Tele-entrega até 3 km*: R$ 5,00\n• *Tele-entrega 3-5 km*: R$ 8,00\n• *Acima de 5 km*: sob consulta\n\nPedido mínimo p/ entrega: R$ 25,00\nPrazo: 30 a 60 min conforme demanda.`
}

function tplPagamento() {
  return `💳 *Formas de pagamento*\n\n• *Pix* (recomendado, libera o pedido na hora)\n• *Cartão de crédito* (no app ou maquininha)\n• *Cartão de débito* (maquininha)\n• *Dinheiro* (avise se precisa de troco)\n• *Boleto* só pra PJ acima de R$ 200\n\nQual prefere?`
}

function tplMenu() {
  return `Oi! 😊 Posso te ajudar com:\n\n📦 Status do seu pedido (ex: "cotação #1")\n🛒 Buscar produto (ex: "tem cerveja gelada?")\n🚚 Info sobre entrega e prazos\n💳 Formas de pagamento\n🕐 Horário de funcionamento\n\nÉ só me dizer o que precisa!`
}

function tplCotacao(c, itens) {
  // c: { cotacao_id, cotacao_status, total, pedido_protocolo, pedido_status, forma_pagto, tipo_entrega, financeiro_status, criado_em }
  // itens: [{ nome_produto, quantidade, subtotal }]
  const linhas = []
  const ref = c.pedido_protocolo ? `*${c.pedido_protocolo}*` : `Cotação *#${c.cotacao_id}*`
  linhas.push(`${ref}`)
  if (c.pedido_status && FASE_NATURAL[c.pedido_status]) {
    linhas.push(FASE_NATURAL[c.pedido_status])
  } else if (c.cotacao_status) {
    linhas.push(`Status da cotação: *${c.cotacao_status}*`)
  }
  if (c.tipo_entrega) linhas.push(`Modalidade: ${c.tipo_entrega === 'retirada' ? '🏪 retirada na loja' : '🚚 tele-entrega'}`)
  if (c.forma_pagto) {
    const pago = c.financeiro_status && STATUS_FINANCEIRO_TXT[c.financeiro_status]
    linhas.push(`Pagamento: ${c.forma_pagto.toUpperCase()}${pago ? ` (${pago})` : ''}`)
  }
  if (c.total) linhas.push(`Total: R$ ${parseFloat(c.total).toFixed(2)}`)

  // Lista de produtos (se houver)
  if (Array.isArray(itens) && itens.length > 0) {
    linhas.push('') // linha em branco antes da lista
    linhas.push('🛒 *Itens:*')
    itens.forEach(it => {
      const qty = parseFloat(it.quantidade)
      const qtyStr = Number.isInteger(qty) ? `${qty}x` : `${qty}`
      const sub = it.subtotal != null ? ` — R$ ${parseFloat(it.subtotal).toFixed(2)}` : ''
      linhas.push(`• ${qtyStr} ${it.nome_produto}${sub}`)
    })
  }

  // Pergunta de fechamento conforme status
  if (c.pedido_status === 'entregue') linhas.push(`\nVocê chegou a receber tudo certinho? 🙏`)
  else if (c.pedido_status === 'expedicao') linhas.push(`\nQualquer coisa é só me chamar!`)
  return linhas.join('\n')
}

// Busca itens de um pedido ou cotação
async function lookupItens({ pedido_id, cotacao_id }, tenantId = 1) {
  if (pedido_id) {
    return await query(
      `SELECT nome_produto, quantidade, subtotal
       FROM pedido_itens pi
       JOIN pedidos p ON p.id = pi.pedido_id
       WHERE pi.pedido_id = $1 AND p.tenant_id = $2
       ORDER BY pi.id`,
      [pedido_id, tenantId],
    ).catch(() => [])
  }
  if (cotacao_id) {
    return await query(
      `SELECT nome_produto, quantidade, subtotal
       FROM cotacao_itens ci
       JOIN cotacoes c ON c.id = ci.cotacao_id
       WHERE ci.cotacao_id = $1 AND c.tenant_id = $2
       ORDER BY ci.id`,
      [cotacao_id, tenantId],
    ).catch(() => [])
  }
  return []
}

function tplMeusPedidos(pedidos) {
  if (!pedidos || pedidos.length === 0) {
    return `Não encontrei pedidos no seu histórico ainda. Quer começar um agora? 🛒`
  }
  const linhas = ['📋 *Seus últimos pedidos*\n']
  pedidos.slice(0, 5).forEach(p => {
    const fase = FASE_NATURAL[p.status]?.replace(/[📦🎁🚚✅]/g, '').trim() || p.status
    linhas.push(`• *${p.protocolo}* (${p.data}) — ${fase}`)
  })
  linhas.push('\nQuer detalhes de algum? Me diga o número.')
  return linhas.join('\n')
}

// ── Lookups ─────────────────────────────────────────────────────────────────

async function lookupCotacaoOuPedido(intent, tenantId = 1) {
  if (intent.tipo === 'cotacao_id') {
    return await queryOne(
      `SELECT c.id AS cotacao_id, c.status AS cotacao_status, c.total,
              p.id AS pedido_id, p.protocolo AS pedido_protocolo, p.status AS pedido_status,
              p.forma_pagto, p.tipo_entrega, p.financeiro_status
       FROM cotacoes c
       LEFT JOIN pedidos p ON p.cotacao_id = c.id AND p.tenant_id = c.tenant_id
       WHERE c.id = $1 AND c.tenant_id = $2
       ORDER BY p.id DESC LIMIT 1`,
      [intent.valor, tenantId],
    ).catch(() => null)
  }
  if (intent.tipo === 'protocolo') {
    return await queryOne(
      `SELECT cotacao_id, NULL AS cotacao_status, total,
              id AS pedido_id, protocolo AS pedido_protocolo, status AS pedido_status,
              forma_pagto, tipo_entrega, financeiro_status
       FROM pedidos WHERE protocolo ILIKE $1 AND tenant_id = $2 LIMIT 1`,
      [intent.valor, tenantId],
    ).catch(() => null)
  }
  if (intent.tipo === 'pedido_id') {
    return await queryOne(
      `SELECT cotacao_id, NULL AS cotacao_status, total,
              id AS pedido_id, protocolo AS pedido_protocolo, status AS pedido_status,
              forma_pagto, tipo_entrega, financeiro_status
       FROM pedidos WHERE id = $1 AND tenant_id = $2 LIMIT 1`,
      [intent.valor, tenantId],
    ).catch(() => null)
  }
  return null
}

async function lookupUltimosPedidos(phone, tenantId = 1) {
  if (!phone) return []
  // Tenta achar user pelo telefone
  const user = await queryOne(
    `SELECT id FROM users
     WHERE REGEXP_REPLACE(telefone, '[^0-9]', '', 'g') LIKE $1 LIMIT 1`,
    [`%${phone}`],
  ).catch(() => null)
  if (!user) return []
  return await query(
    `SELECT protocolo, status, total,
            TO_CHAR(created_at, 'DD/MM') AS data
     FROM pedidos WHERE user_id = $1 AND tenant_id = $2
     ORDER BY created_at DESC LIMIT 5`,
    [user.id, tenantId],
  ).catch(() => [])
}

// ── Roteador principal ──────────────────────────────────────────────────────

/**
 * Tenta responder a mensagem localmente sem chamar o LLM.
 * @param {string} msg — mensagem do usuário
 * @param {string} fromPhone — número do remetente (digits-only ou JID)
 * @param {number} tenantId
 * @returns {Promise<{reply: string, intent: string} | null>}
 */
async function tryLocalReply(msg, fromPhone, tenantId = 1) {
  if (!msg || !msg.trim()) return null
  const m = msg.trim()
  const phone = (fromPhone || '').replace(/\D/g, '').replace(/^55/, '')

  // 1. Cotação/Pedido por ID — alta prioridade
  const cot = intentCotacaoOuPedido(m)
  if (cot && cot.tipo !== 'ultimo_do_cliente') {
    const data = await lookupCotacaoOuPedido(cot, tenantId)
    if (data) {
      const itens = await lookupItens({ pedido_id: data.pedido_id, cotacao_id: data.cotacao_id }, tenantId)
      return { reply: tplCotacao(data, itens), intent: 'cotacao_lookup' }
    }
    // Não achou — deixa o LLM lidar (pode pedir mais detalhes)
    return null
  }
  if (cot?.tipo === 'ultimo_do_cliente') {
    const lista = await lookupUltimosPedidos(phone, tenantId)
    if (lista.length > 0) {
      const last = await queryOne(
        `SELECT cotacao_id, NULL AS cotacao_status, total,
                id AS pedido_id, protocolo AS pedido_protocolo, status AS pedido_status,
                forma_pagto, tipo_entrega, financeiro_status
         FROM pedidos WHERE protocolo = $1 AND tenant_id = $2 LIMIT 1`,
        [lista[0].protocolo, tenantId],
      ).catch(() => null)
      if (last) {
        const itens = await lookupItens({ pedido_id: last.pedido_id, cotacao_id: last.cotacao_id }, tenantId)
        return { reply: tplCotacao(last, itens), intent: 'cotacao_lookup_ultimo' }
      }
    }
    return null
  }

  // 2. FAQ determinísticos (mensagens curtas e objetivas)
  if (intentMenu(m))      return { reply: tplMenu(),      intent: 'menu' }
  if (intentHorario(m))   return { reply: tplHorario(),   intent: 'horario' }
  if (intentEndereco(m))  return { reply: tplEndereco(),  intent: 'endereco' }
  if (intentEntrega(m))   return { reply: tplEntrega(),   intent: 'entrega' }
  if (intentPagamento(m)) return { reply: tplPagamento(), intent: 'pagamento' }

  // 3. Meus pedidos
  if (intentMeusPedidos(m)) {
    const lista = await lookupUltimosPedidos(phone, tenantId)
    return { reply: tplMeusPedidos(lista), intent: 'meus_pedidos' }
  }

  return null
}

// ── Envio direto via motor (bypassa /secretaria/ask) ────────────────────────

async function sendLocalReply(motorUrl, integKey, instanceId, chatId, reply, intent, userMsg) {
  try {
    const res = await fetch(`${motorUrl}/api/ext/v1/inbox/${encodeURIComponent(chatId)}/send`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'x-nw-key': integKey },
      body: JSON.stringify({ text: reply }),
    })
    const status = res.ok ? 'sent_local' : 'failed_local'
    await execute(
      `INSERT INTO nw_auto_replies (instance_id, chat_id, conv_id, user_msg, reply, status)
       VALUES ($1, $2, NULL, $3, $4, $5)`,
      [instanceId || '', chatId, userMsg, reply, status],
    )
    // Telemetria: baseline ~5000 chars de prompt seriam enviados ao LLM
    await execute(
      `INSERT INTO nw_router_metrics (chat_id, handler, intent, user_msg, reply_chars, ctx_chars, saved_chars)
       VALUES ($1, 'local', $2, $3, $4, 0, 5000)`,
      [chatId, intent, userMsg.slice(0, 200), reply.length],
    ).catch(() => {})
    if (res.ok) {
      console.log(`[smart-router] ✅ [${intent}] ${chatId} → "${reply.slice(0, 50)}…" (0 tokens)`)
      return true
    }
    console.warn(`[smart-router] envio falhou: ${res.status}`)
    return false
  } catch (err) {
    console.error('[smart-router] erro ao enviar:', err.message)
    return false
  }
}

module.exports = { tryLocalReply, sendLocalReply }
