import ChatBot from '#amidala/wcs/shared/chat-bot/chat-bot.js'
import KoreaiTransport from './koreai-transport.js'
import { marked } from 'marked'

const { h, preactHooks, matter, register } = globalThis.ami
const { useState } = preactHooks
const { Button, RichText, Img } = matter

const THINKING_MESSAGE = { isBot: true, children: ['...'] }

function setupMarkdown () {
  // Remove code parsing.
  const tokenizer = {
    code () {
      return null
    }
  }

  const renderer = {
    // Open all links in new tab.
    link (args) {
      const processedLink = this.constructor.prototype.link.call(this, args)
      return processedLink.replace('<a ', '<a target="_blank" ')
    },
    // Replace italics with bold.
    em ({ tokens }) {
      const text = this.parser.parseInline(tokens)
      return `<strong>${text}</strong>`
    }
  }

  marked.use({ tokenizer, renderer })
}

setupMarkdown()

function ChatbotKoreai ({
  id,
  url,
  name,
  icon,
  'button-text': buttonText,
  'header-text': headerText,
  'attach-text': attachText,
  'input-text': inputText,
  'send-text': sendText
}) {
  if (!id || !url) {
    console.error('Both id and url are required.')
    return null
  }

  const [state, setState] = useState({
    transport: new KoreaiTransport(id, name, url)
  })

  // Translate Kore.ai messages onto chat-bot.js messages.
  const [messages, isBusy] = getMessagesFromHistory(state.transport.history, userMessage => {
    // Additional button handling.
    state.transport.sendMessage({ message: { body: userMessage } })
    setState({ ...state })
  })

  return h(ChatBot, {
    buttonText,
    headerText,
    attachText,
    inputText,
    sendText,
    isBusy: isBusy || !state.transport.isOpen(),
    icon,
    messages,
    onInit () {
      state.transport.connect(() => setState({ ...state })) // Redraw on new message.
    },
    onSend (userMessage) {
      state.transport.sendMessage({ message: { body: userMessage } })
      setState({ ...state }) // Redraw on message sent.
    },
    onClose () {
      state.transport.disconnect()
    },
    async onAttach (file) {
      await state.transport.sendFile(file)
      setState({ ...state }) // Redraw on file attached.
    }
  })
}

function getMessagesFromHistory (history, onSend) {
  const messages = []

  for (const data of history) {
    if (data.type === 'bot_response') {
      messages.push(...resolveBotMessages(data, onSend))
    } else if (data.resourceid === '/bot.message' && data?.message?.body) {
      messages.push({
        children: data.message.body,
        isBot: false,
        timestamp: new Date(data.id)
      })
    }
  }

  const isBusy = history.length < 4
  if (isBusy) {
    messages.push(THINKING_MESSAGE)
  }

  return [messages, isBusy]
}

function resolveBotMessages (eventData, onSend) {
  const messages = []

  for (const message of eventData?.message ?? []) {
    const outerPayload = message?.component?.payload
    const innerPayload = outerPayload?.payload
    const payload = innerPayload ?? outerPayload
    const text = resolveText(payload)
    const buttonTitles = resolveButtonTitles(payload)
    const imageModel = outerPayload?.type === 'image' && { src: payload.url }

    messages.push({
      children: h('div', { className: 'space-y-2' },
        // Image.
        imageModel && h('div', { className: 'rounded-lg overflow-hidden' },
          h(Img, { _model: imageModel })
        ),

        // Text message.
        text && h(RichText, marked(text)),

        // Buttons.
        buttonTitles.length > 0 && h('div', { className: 'flex flex-col', style: 'gap: 8px' }, buttonTitles.map(buttonTitle =>
          h('div',
            h(Button, {
              _model: { name: buttonTitle },
              _size: 'xs',
              async onClick () {
                onSend(buttonTitle)
              }
            })
          )
        ))
      ),
      isBot: eventData.type === 'bot_response',
      timestamp: new Date(eventData.timestamp)
    })
  }

  return messages
}

function resolveButtonTitles (payload) {
  const buttonTitles = []
  for (const reply of payload?.quick_replies ?? []) {
    buttonTitles.push(reply.title)
  }

  for (const feedback of payload?.thumpsUpDownArrays ?? []) {
    buttonTitles.push(feedback.reviewText)
  }

  return buttonTitles
}

function resolveText (payload) {
  if (typeof payload === 'string') {
    return payload
  }

  return payload?.text
}

register(ChatbotKoreai, 'chatbot-koreai')
