/**
 * rich-message.ts — Construção e envio de mensagens ricas via Baileys padrão
 *
 * O pacote @whiskeysockets/baileys não suporta os campos `nativeButtons`,
 * `nativeList` e `nativeCarousel` que são específicos do fork InfiniteAPI.
 * Este módulo replica a lógica de transformação do InfiniteAPI para que
 * o motor newwhats possa enviar botões, listas e carrosséis usando o
 * sock.relayMessage() com o proto correto.
 *
 * Referência: github:rsalcara/InfiniteAPI — lib/Socket/messages-send.js
 *
 * IMPORTANTE: O WhatsApp Web NÃO suporta viewOnceMessage para mensagens interativas.
 * Por isso usamos interactiveMessage DIRETO (sem wrapper viewOnce) + additionalNodes.
 *
 * O que é necessário para botões/listas/carrosséis funcionarem no WhatsApp
 * (celular E web):
 *
 *  1. interactiveMessage direto (NÃO viewOnceMessage) — viewOnce não carrega no Web
 *
 *  2. Nó adicional <biz><interactive type="native_flow" v="1"><native_flow v="9" name="mixed"/></interactive></biz>
 *     injetado no stanza via relayMessage(additionalNodes)
 *     (sem isso o WhatsApp Web exibe "Não foi possível carregar")
 *
 *  3. Nó <bot biz_bot="1"/> para chats 1-a-1
 *     (necessário para interactive messages em conversas privadas)
 *
 *  Lista (single_select) também usa o mesmo fluxo — InfiniteAPI converte
 *  o viewOnceMessage > nativeFlowMessage de volta para listMessage no relayMessage,
 *  mas testamos que a forma direta com nó biz > list também funciona.
 */
import { generateMessageIDV2, generateWAMessageFromContent, isJidGroup, isJidStatusBroadcast } from './engine'
import { proto } from './engine'
import type { WASocket, BinaryNode } from './engine'

// ─── Tipos de botão (espelho do formato nativo InfiniteAPI) ──────────────────
type NativeBtn =
  | { type: 'reply';  id: string;          text: string }
  | { type: 'url';    text: string;        url: string }
  | { type: 'copy';   text: string;        copyText: string }
  | { type: 'call';   text: string;        phoneNumber: string }

// ─── Payload rico de entrada (o que o frontend/ext-api envia) ────────────────
export interface RichPayload {
  text?:          string
  footer?:        string
  nativeButtons?: NativeBtn[]
  nativeList?: {
    buttonText: string
    sections: Array<{
      title: string
      rows: Array<{ id: string; title: string; description?: string }>
    }>
  }
  nativeCarousel?: {
    cards: Array<{
      title?:   string
      body?:    string
      footer?:  string
      image?:   { url: string }
      buttons:  Array<{ type?: string; id?: string; text: string }>
    }>
  }
  poll?: {
    name:             string
    values:           string[]
    selectableCount?: number
  }
}

// ─── Converte um botão nativo para o formato buttonParamsJson do WhatsApp ─────
function formatNativeFlowButton(btn: NativeBtn): { name: string; buttonParamsJson: string } {
  switch (btn.type) {
    case 'url':
      return {
        name: 'cta_url',
        buttonParamsJson: JSON.stringify({
          display_text:  btn.text,
          url:           btn.url,
          merchant_url:  btn.url,
        }),
      }
    case 'copy':
      return {
        name: 'cta_copy',
        buttonParamsJson: JSON.stringify({
          display_text: btn.text,
          copy_code:    btn.copyText,
        }),
      }
    case 'reply':
      return {
        name: 'quick_reply',
        buttonParamsJson: JSON.stringify({
          display_text: btn.text,
          id:           btn.id,
        }),
      }
    case 'call':
      return {
        name: 'cta_call',
        buttonParamsJson: JSON.stringify({
          display_text:  btn.text,
          phone_number:  btn.phoneNumber,
        }),
      }
  }
}

// ─── Nó biz > interactive > native_flow (obrigatório para renderizar no Web) ──
const BIZ_NATIVE_FLOW_NODE: BinaryNode = {
  tag: 'biz',
  attrs: {},
  content: [
    {
      tag: 'interactive',
      attrs: { type: 'native_flow', v: '1' },
      content: [
        {
          tag: 'native_flow',
          attrs: { v: '9', name: 'mixed' },
        },
      ],
    },
  ],
}

// ─── Nó biz para listMessage legado (formato que renderiza no Web) ───────────
// InfiniteAPI usa <biz><list type="product_list" v="2"/></biz> com proto.listMessage
// (não interactiveMessage). Comentário do fork: "Modern format causes rejection
// (error 479) — Legacy format works on all platforms".
const BIZ_LIST_NODE: BinaryNode = {
  tag: 'biz',
  attrs: {},
  content: [
    {
      tag: 'list',
      attrs: { type: 'product_list', v: '2' },
    },
  ],
}

// ─── Nó bot (necessário para chats 1-a-1 com interactive messages) ────────────
const BOT_NODE: BinaryNode = {
  tag: 'bot',
  attrs: { biz_bot: '1' },
}

// ─── Helper: jid é chat privado (não grupo, não status, não bot) ─────────────
function isPrivateChat(jid: string): boolean {
  return (
    !isJidGroup(jid) &&
    !isJidStatusBroadcast(jid) &&
    !jid.includes('newsletter') &&
    !jid.includes('broadcast')
  )
}

// ─── Helper: envia proto pré-construído pelo MESMO caminho do sendMessage ────
// Diferença crítica vs `sock.relayMessage` direto:
//   sendMessage()  →  generateWAMessage()  →  WAProto.Message.fromObject()  →  relayMessage()
//                                            ↑ normalização proto que o WA Web exige
// Sem esse passo, o Web descarta a mensagem ("Não foi possível carregar").
// É o mesmo motivo pelo qual nossas enquetes (via sendMessage) funcionam no Web
// e os botões (via relayMessage direto) não funcionavam.
async function sendBuiltProto(
  sock:            WASocket,
  jid:             string,
  protoMsg:        any,
  msgId:           string,
  additionalNodes: BinaryNode[],
  quoted?:         { key: any; message: any },
): Promise<string> {
  const fullMsg = generateWAMessageFromContent(jid, protoMsg, {
    userJid:   sock.user?.id,
    messageId: msgId,
    quoted,
  } as any)

  await sock.relayMessage(jid, fullMsg.message as any, {
    messageId: fullMsg.key.id!,
    additionalNodes,
  })
  return fullMsg.key.id!
}

// ─── Helper: monta interactiveMessage DIRETO (sem viewOnceMessage wrapper) ────
// viewOnceMessage wrapper faz o WhatsApp Web exibir "Não foi possível carregar":
// o Web trata viewOnce como mensagem efêmera e recusa conteúdo interativo dentro.
// Baileys padrão não adiciona messageContextInfo para interactiveMessage
// (só faz isso para poll, com messageSecret). Usar interactiveMessage direto
// + nó <biz> no additionalNodes é o caminho correto com @whiskeysockets/baileys.
function makeInteractiveProto(interactiveMsg: any): any {
  return {
    interactiveMessage: interactiveMsg,
  }
}

/**
 * Envia uma mensagem rica pelo socket Baileys padrão.
 *
 * - Buttons / CTAs: interactiveMessage direto + nó biz>interactive>native_flow + nó bot
 * - Carousel:       interactiveMessage direto + nó biz>interactive>native_flow
 * - List:           interactiveMessage direto + nó biz>interactive>native_flow + nó bot
 * - Poll:           sock.sendMessage (requer encriptação especial do Baileys)
 * - Text:           sock.sendMessage normal
 *
 * Retorna o messageId gerado.
 */
export async function sendRichMessage(
  sock:       WASocket,
  jid:        string,
  payload:    RichPayload,
  quoted?:    { key: any; message: any },
  engineType?: string,
): Promise<string> {
  const { text, footer, nativeButtons, nativeList, nativeCarousel, poll } = payload
  const engine = engineType ?? process.env.BAILEYS_ENGINE ?? 'infinite'

  // Se a engine ativa for 'infinite', utilizamos o suporte nativo do fork InfiniteAPI
  if (engine === 'infinite') {
    // ── Poll: delegado ao sendMessage padrão (requer encriptação especial do Baileys) ──
    if (poll) {
      const sent = await sock.sendMessage(
        jid,
        {
          poll: {
            name:             poll.name,
            values:           poll.values,
            selectableCount:  Math.min(Math.max(1, poll.selectableCount ?? 1), poll.values.length),
          },
        },
        quoted ? { quoted } : undefined,
      )
      return sent?.key?.id ?? `poll-${Date.now()}`
    }

    // ── Botões / CTAs nativos (InfiniteAPI) ──
    if (nativeButtons) {
      const sent = await sock.sendMessage(
        jid,
        {
          text: text ?? '',
          footer,
          nativeButtons,
        } as any,
        quoted ? { quoted } : undefined,
      )
      return sent?.key?.id ?? `btn-${Date.now()}`
    }

    // ── Carrossel nativo (InfiniteAPI) ──
    if (nativeCarousel) {
      const sent = await sock.sendMessage(
        jid,
        {
          text: text ?? '',
          footer,
          nativeCarousel,
        } as any,
        quoted ? { quoted } : undefined,
      )
      return sent?.key?.id ?? `carousel-${Date.now()}`
    }

    // ── Lista nativa (InfiniteAPI) ──
    if (nativeList) {
      const sent = await sock.sendMessage(
        jid,
        {
          text: text ?? '',
          footer,
          nativeList,
        } as any,
        quoted ? { quoted } : undefined,
      )
      return sent?.key?.id ?? `list-${Date.now()}`
    }

    // ── Texto simples (InfiniteAPI) ──
    const sent = await sock.sendMessage(
      jid,
      { text: text ?? '' },
      quoted ? { quoted } : undefined,
    )
    return sent?.key?.id ?? `text-${Date.now()}`
  }

  // FALLBACK: Lógica de montagem manual de nós binários para o Baileys oficial
  // ── Poll: delegado ao sendMessage padrão (requer messageSecret) ───────────
  if (poll) {
    const sent = await sock.sendMessage(
      jid,
      {
        poll: {
          name:             poll.name,
          values:           poll.values,
          selectableCount:  Math.min(Math.max(1, poll.selectableCount ?? 1), poll.values.length),
        },
      },
      quoted ? { quoted } : undefined,
    )
    return sent?.key?.id ?? `poll-${Date.now()}`
  }

  // ── Texto simples: delegado ao sendMessage padrão ─────────────────────────
  if (!nativeButtons && !nativeList && !nativeCarousel) {
    const sent = await sock.sendMessage(
      jid,
      { text: text ?? '' },
      quoted ? { quoted } : undefined,
    )
    return sent?.key?.id ?? `text-${Date.now()}`
  }

  const msgId     = generateMessageIDV2(sock.user?.id)
  const isPrivate = isPrivateChat(jid)

  // ── Botões / CTAs: interactiveMessage + nativeFlowMessage ────────────────────
  if (nativeButtons) {
    const formattedButtons = nativeButtons.map(formatNativeFlowButton)

    const protoMsg = makeInteractiveProto({
      body:   { text: text ?? '' },
      footer: footer ? { text: footer } : undefined,
      header: { title: '', subtitle: '', hasMediaAttachment: false },
      nativeFlowMessage: {
        buttons:           formattedButtons,
        messageParamsJson: JSON.stringify({}),
        messageVersion:    2,
      },
    })

    const additionalNodes: BinaryNode[] = [BIZ_NATIVE_FLOW_NODE]
    return await sendBuiltProto(sock, jid, protoMsg, msgId, additionalNodes, quoted)
  }

  // ── Carrossel: viewOnceMessage > interactiveMessage > carouselMessage ─────
  if (nativeCarousel) {
    const cards = nativeCarousel.cards
    if (cards.length < 2) throw new Error('Carrossel requer no mínimo 2 cards')
    if (cards.length > 10) throw new Error('Carrossel suporta no máximo 10 cards')

    const carouselCards: any[] = cards.map((card) => {
      const rawButtons = card.buttons ?? []
      const cardButtons = rawButtons.length > 0
        ? rawButtons.map((btn) => formatNativeFlowButton({
            type: (btn.type as any) ?? 'reply',
            id:   btn.id ?? btn.text.toLowerCase().replace(/\s+/g, '_'),
            text: btn.text,
          } as any))
        : [{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'Ver mais', id: 'see_more' }) }]

      return {
        header: {
          title:              card.title ?? '',
          subtitle:           '',
          hasMediaAttachment: !!(card.image?.url),
        },
        body:   { text: card.body ?? '' },
        footer: card.footer ? { text: card.footer } : undefined,
        nativeFlowMessage: {
          buttons:           cardButtons,
          messageParamsJson: JSON.stringify({}),
        },
      }
    })

    const protoMsg = makeInteractiveProto({
      body:   { text: text ?? '' },
      footer: footer ? { text: footer } : undefined,
      header: { title: '', subtitle: '', hasMediaAttachment: false },
      carouselMessage: {
        cards:          carouselCards,
        messageVersion: 1,
      },
    })

    const additionalNodes: BinaryNode[] = [BIZ_NATIVE_FLOW_NODE]
    return await sendBuiltProto(sock, jid, protoMsg, msgId, additionalNodes, quoted)
  }

  // ── Lista: proto.listMessage LEGADO + nó <biz><list type="product_list" v="2"/></biz> ──
  if (nativeList) {
    const protoMsg: any = {
      listMessage: {
        title:       '',
        description: text ?? '',
        buttonText:  nativeList.buttonText,
        footerText:  footer ?? '',
        listType:    2,
        sections:    nativeList.sections.map((s) => ({
          title: s.title,
          rows:  s.rows.map((r) => ({
            rowId:       r.id,
            title:       r.title,
            description: r.description ?? '',
          })),
        })),
      },
    }

    const additionalNodes: BinaryNode[] = [BIZ_LIST_NODE]
    if (isPrivate) additionalNodes.push(BOT_NODE)

    return await sendBuiltProto(sock, jid, protoMsg, msgId, additionalNodes, quoted)
  }

  // Fallback (não deve chegar aqui)
  const sent = await sock.sendMessage(jid, { text: text ?? '' })
  return sent?.key?.id ?? `fallback-${Date.now()}`
}
