import * as sdk from '@botpress/sdk'
import { RuntimeError } from '@botpress/sdk'
import { GoogleClient } from '../google-api'
import {
  composeRawEmail,
  generateAudioMessage,
  generateCardMessage,
  generateCarouselMessage,
  generateFileDownloadMessage,
  generateImageMessage,
  generateLocationMessage,
  generateMarkdownMessage,
  generateVideoMessage,
} from '../utils/mail-composing'
import { wrapChannel } from './channel-wrapper'
import * as bp from '.botpress'

export const channels = {
  channel: {
    messages: {
      image: wrapChannel({ channelName: 'channel', messageType: 'image' }, (props) => {
        const { imageUrl, title: altText } = props.payload
        const htmlContent = generateImageMessage({ imageUrl, altText: altText ?? 'image' })
        const textContent = `Image:\n${imageUrl}`

        return _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      audio: wrapChannel({ channelName: 'channel', messageType: 'audio' }, (props) => {
        const { audioUrl, title } = props.payload
        const htmlContent = generateAudioMessage({ audioUrl, title: title ?? 'Play audio file' })
        const textContent = `Audio file:\n${audioUrl}`

        return _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      video: wrapChannel({ channelName: 'channel', messageType: 'video' }, (props) => {
        const { videoUrl, title } = props.payload
        const htmlContent = generateVideoMessage({ videoUrl, title: title ?? 'Play video file' })
        const textContent = `Video file:\n${videoUrl}`

        return _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      file: wrapChannel({ channelName: 'channel', messageType: 'file' }, (props) => {
        const { fileUrl, title } = props.payload
        const htmlContent = generateFileDownloadMessage({ fileUrl, title: title ?? 'Download file' })
        const textContent = `Linked file:\n${fileUrl}`

        return _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      text: wrapChannel({ channelName: 'channel', messageType: 'text' }, async (props) => {
        const { text: textContent } = props.payload

        await _sendEmailReply({
          ...props,
          textContent,
        })
      }),
      choice: wrapChannel({ channelName: 'channel', messageType: 'choice' }, async (props) => {
        const { text, options } = props.payload
        let content = `${text}\n`

        for (const option of options) {
          content += `- ${option.label}\n`
        }

        await _sendEmailReply({
          ...props,
          textContent: content,
        })
      }),
      markdown: wrapChannel({ channelName: 'channel', messageType: 'markdown' }, async (props) => {
        const { markdown } = props.payload
        const htmlContent = generateMarkdownMessage({ markdown })
        const textContent = markdown

        await _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      location: wrapChannel({ channelName: 'channel', messageType: 'location' }, async (props) => {
        const { latitude, longitude, address, title } = props.payload
        const htmlContent = generateLocationMessage({ latitude, longitude, address, title })
        const textContent = `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`

        await _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      card: wrapChannel({ channelName: 'channel', messageType: 'card' }, async (props) => {
        const { title, subtitle, imageUrl, actions } = props.payload

        const htmlContent = generateCardMessage({ title, subtitle: subtitle ?? '', imageUrl: imageUrl ?? '', actions })
        const textContent = `${title}\n${subtitle}\n\n${actions.map((a) => `${a.label}: ${a.value}`).join('\n')}`

        await _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      carousel: wrapChannel({ channelName: 'channel', messageType: 'carousel' }, async (props) => {
        const { items: cards } = props.payload

        const htmlContent = generateCarouselMessage({
          cards: cards.map((c) => ({ ...c, subtitle: c.subtitle ?? '', imageUrl: c.imageUrl ?? '' })),
        })
        const textContent = cards
          .map((c) => `${c.title}\n${c.subtitle}\n\n${c.actions.map((a) => `${a.label}: ${a.value}`).join('\n')}`)
          .join('\n\n\n')

        await _sendEmailReply({
          ...props,
          textContent,
          htmlContent,
        })
      }),
      dropdown: wrapChannel({ channelName: 'channel', messageType: 'dropdown' }, () => {
        throw new sdk.RuntimeError('This message type is not yet implemented')
      }),
      bloc: wrapChannel({ channelName: 'channel', messageType: 'bloc' }, () => {
        throw new sdk.RuntimeError('This message type is not yet implemented')
      }),
    },
  },
} as const satisfies bp.IntegrationProps['channels']

const _sendEmailReply = async ({
  conversation,
  ack,
  textContent,
  htmlContent,
  inReplyTo,
  googleClient,
}: bp.AnyMessageProps & {
  logger: bp.Logger
  textContent: string
  htmlContent?: string
  inReplyTo: string
  googleClient: GoogleClient
}) => {
  const { threadId, email, subject, references, cc } = _getConversationInfo(conversation)

  const raw = await composeRawEmail({
    to: email,
    subject,
    text: textContent,
    html: htmlContent ?? textContent,
    textEncoding: 'base64',
    inReplyTo,
    references: references ?? inReplyTo,
    cc,
  })

  const res = await googleClient.messages.send(raw, threadId)

  await ack({ tags: { id: `${res.id}` } })
}

const _getConversationInfo = (conversation: bp.AnyMessageProps['conversation']) => {
  const { id, tags } = conversation
  const { id: threadId, subject, email, references, cc } = tags

  if (!(threadId && subject && email)) {
    throw new RuntimeError(`No valid information found for conversation ${id}`)
  }

  return { threadId, subject, email, references, cc }
}
