import { embedMessage, event, Events, normalMessage, UserMessage } from '../utils/index.js'
import { getChannelInfo, getServerConfig, getThread, getUserConfig, openChannelInfo, openConfig, openThreadInfo, ServerConfig, UserConfig } from '../utils/index.js'
import { clean } from '../utils/mentionClean.js'
import { TextChannel, ThreadChannel } from 'discord.js'

/** 
 * Max Message length for free users is 2000 characters (bot or not).
 * Bot supports infinite lengths for normal messages.
 * 
 * @param message the message received from the channel
 */
export default event(Events.MessageCreate, async ({ log, msgHist, tokens, ollama, client }, message) => {
    log(`Message \"${clean(message.content)}\" from ${message.author.tag} in channel/thread ${message.channelId}.`)

    // Do not respond if bot talks in the chat
    if (message.author.username === message.client.user.username) return

    // Only respond if message mentions the bot
    if (!message.mentions.has(tokens.clientUid)) return

    // default stream to false
    let shouldStream = false
 
    try {
        // Retrieve Server/Guild Preferences
        const serverConfig: ServerConfig = await new Promise((resolve, reject) => {
            getServerConfig(`${message.guildId}-config.json`, (config) => {
                // check if config.json exists
                if (config === undefined) {
                    // Allowing chat options to be available
                    openConfig(`${message.guildId}-config.json`, 'toggle-chat', true)

                    // default to channel scope chats
                    openConfig(`${message.guildId}-config.json`, 'channel-toggle', true)
                    reject(new Error('No Server Preferences is set up.\n\nCreating default server preferences file...\nPlease try chatting again.'))
                    return
                }

                // check if chat is disabled
                if (!config.options['toggle-chat']) {
                    reject(new Error('Admin(s) have disabled chat features.\n\n Please contact your server\'s admin(s).'))
                    return
                }

                // ensure channel json exists, if not create it
                if (config.options['channel-toggle']) {
                    openChannelInfo(message.channelId,
                        message.channel as TextChannel, 
                        message.author.username
                    )
                }

                resolve(config)
            })
        })

        // Retrieve User Preferences
        const userConfig: UserConfig = await new Promise((resolve, reject) => {
            getUserConfig(`${message.author.username}-config.json`, (config) => {
                if (config === undefined) {
                    openConfig(`${message.author.username}-config.json`, 'message-style', false)
                    reject(new Error('No User Preferences is set up.\n\nCreating preferences file with \`message-style\` set as \`false\` for regular messages.\nPlease try chatting again.'))
                    return
                }
    
                // check if there is a set capacity in config
                if (typeof config.options['modify-capacity'] !== 'number')
                    log(`Capacity is undefined, using default capacity of ${msgHist.capacity}.`)
                else if (config.options['modify-capacity'] === msgHist.capacity)
                    log(`Capacity matches config as ${msgHist.capacity}, no changes made.`)
                else {
                    log(`New Capacity found. Setting Context Capacity to ${config.options['modify-capacity']}.`)
                    msgHist.capacity = config.options['modify-capacity']
                }
    
                // set stream state
                shouldStream = config.options['message-stream'] as boolean || false
    
                resolve(config)
            })
        })

        

        // need new check for "open/active" threads/channels here!
        const chatMessages: UserMessage[] = await new Promise((resolve) => {
            // set new queue to modify
            if (serverConfig.options['channel-toggle']) {
                getChannelInfo(`${message.channelId}-${message.author.username}.json`, (channelInfo) => {
                    if (channelInfo?.messages)
                        resolve(channelInfo.messages)
                    else
                        log(`Channel ${message.channel}-${message.author.username} does not exist.`)
                })
            } else {
                getThread(`${message.channelId}.json`, (threadInfo) => {
                    if (threadInfo?.messages)
                        resolve(threadInfo.messages)
                    else
                        log(`Thread ${message.channelId} does not exist.`)
                }) 
            }
        })

        // response string for ollama to put its response
        let response: string

        // set up new queue
        msgHist.setQueue(chatMessages)

        // check if we can push, if not, remove oldest
        while (msgHist.size() >= msgHist.capacity) msgHist.dequeue()

        // push user response before ollama query
        msgHist.enqueue({
            role: 'user',
            content: clean(message.content)
        })
        
        // undefined or false, use normal, otherwise use embed
        if (userConfig.options['message-style'])
            response = await embedMessage(message, ollama, tokens, msgHist, shouldStream)
        else
            response = await normalMessage(message, ollama, tokens, msgHist, shouldStream)

        // If something bad happened, remove user query and stop
        if (response == undefined) { msgHist.pop(); return }

        // if queue is full, remove the oldest message
        while (msgHist.size() >= msgHist.capacity) msgHist.dequeue()

        // successful query, save it in context history
        msgHist.enqueue({ 
            role: 'assistant', 
            content: response
        })

        // only update the json on success
        if (serverConfig.options['channel-toggle']) {
            openChannelInfo(message.channelId, 
                message.channel as TextChannel, 
                message.author.tag, 
                msgHist.getItems()
            )
        } else {
            openThreadInfo(`${message.channelId}.json`, 
                client.channels.fetch(message.channelId) as unknown as ThreadChannel, 
                msgHist.getItems()
            )
        }
    } catch (error: any) {
        msgHist.pop() // remove message because of failure
        message.reply(`**Error Occurred:**\n\n**Reason:** *${error.message}*`)
    }
})