import { option, createAction } from '@typebot.io/forge'
import { defaultOpenAIOptions, openAIVoices } from '../constants'
import OpenAI, { ClientOptions } from 'openai'
import { isNotEmpty } from '@typebot.io/lib'
import { createId } from '@typebot.io/lib/createId'
import { uploadFileToBucket } from '@typebot.io/lib/s3/uploadFileToBucket'
import { auth } from '../auth'
import { baseOptions } from '../baseOptions'

export const createSpeech = createAction({
  name: 'Create speech',
  auth,
  baseOptions,
  options: option.object({
    model: option.string.layout({
      fetcher: 'fetchSpeechModels',
      defaultValue: 'tts-1',
      placeholder: 'Select a model',
    }),
    input: option.string.layout({
      label: 'Input',
      inputType: 'textarea',
    }),
    voice: option.enum(openAIVoices).layout({
      label: 'Voice',
      placeholder: 'Select a voice',
    }),
    saveUrlInVariableId: option.string.layout({
      inputType: 'variableDropdown',
      label: 'Save URL in variable',
    }),
  }),
  getSetVariableIds: (options) =>
    options.saveUrlInVariableId ? [options.saveUrlInVariableId] : [],
  fetchers: [
    {
      id: 'fetchSpeechModels',
      dependencies: ['baseUrl', 'apiVersion'],
      fetch: async ({ credentials, options }) => {
        const baseUrl = options?.baseUrl ?? defaultOpenAIOptions.baseUrl
        const config = {
          apiKey: credentials.apiKey,
          baseURL: baseUrl ?? defaultOpenAIOptions.baseUrl,
          defaultHeaders: {
            'api-key': credentials.apiKey,
          },
          defaultQuery: options?.apiVersion
            ? {
                'api-version': options.apiVersion,
              }
            : undefined,
        } satisfies ClientOptions

        const openai = new OpenAI(config)

        const models = await openai.models.list()

        return (
          models.data
            .filter((model) => model.id.includes('tts'))
            .sort((a, b) => b.created - a.created)
            .map((model) => model.id) ?? []
        )
      },
    },
  ],
  run: {
    server: async ({ credentials: { apiKey }, options, variables, logs }) => {
      if (!options.input) return logs.add('Create speech input is empty')
      if (!options.voice) return logs.add('Create speech voice is empty')
      if (!options.saveUrlInVariableId)
        return logs.add('Create speech save variable is empty')

      const config = {
        apiKey,
        baseURL: options.baseUrl ?? defaultOpenAIOptions.baseUrl,
        defaultHeaders: {
          'api-key': apiKey,
        },
        defaultQuery: isNotEmpty(options.apiVersion)
          ? {
              'api-version': options.apiVersion,
            }
          : undefined,
      } satisfies ClientOptions

      const openai = new OpenAI(config)

      const model = options.model ?? defaultOpenAIOptions.voiceModel

      const rawAudio = (await openai.audio.speech.create({
        input: options.input,
        voice: options.voice,
        model,
      })) as any

      const url = await uploadFileToBucket({
        file: Buffer.from((await rawAudio.arrayBuffer()) as ArrayBuffer),
        key: `tmp/openai/audio/${createId() + createId()}.mp3`,
        mimeType: 'audio/mpeg',
      })

      variables.set(options.saveUrlInVariableId, url)
    },
  },
})
