console.time('Worker Start')
const {Client, GatewayIntentBits, EmbedBuilder} = require('discord.js');
const {createAudioPlayer, NoSubscriberBehavior, createAudioResource, joinVoiceChannel,VoiceConnectionStatus, AudioPlayerStatus, AudioResource} = require('@discordjs/voice');


const bot = new Client({ intents: [GatewayIntentBits.Guilds,  
    GatewayIntentBits.GuildMembers,
    GatewayIntentBits.GuildBans,
    GatewayIntentBits.GuildEmojisAndStickers,
    GatewayIntentBits.GuildIntegrations ,
    GatewayIntentBits.GuildWebhooks ,
    GatewayIntentBits.GuildInvites ,
    GatewayIntentBits.GuildVoiceStates,
    GatewayIntentBits.GuildPresences,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.GuildMessageReactions,
    GatewayIntentBits.GuildMessageTyping,
    GatewayIntentBits.DirectMessages,
    GatewayIntentBits.DirectMessageReactions,
    GatewayIntentBits.DirectMessageTyping,
    GatewayIntentBits.MessageContent,
    GatewayIntentBits.GuildScheduledEvents,
    GatewayIntentBits.AutoModerationConfiguration,
    GatewayIntentBits.AutoModerationExecution]});
const mongoose = require('mongoose');
const QueueM = require('./models/queue');

const Command = require('./models/mcommands');

const ytdl = require('ytdl-core');

const guild = process.argv[2];
const name = process.argv[3];

var Pause = false;
var currentID;

bot.login(process.env.discord_token).then(()=>
{
    
    console.log('WORKER:',name,'- Music: Guild: (', guild,')');
    console.timeEnd('Worker Start')
    mongoose.Promise = global.Promise;
    mongoose.connect(process.env.mongoDB, {useNewUrlParser: true, useUnifiedTopology:true}).then(console.log('WORKER:',name,'- MONGODB: Connected')).catch(err=>console.log(err));
    music();
});
function music()
{
    QueueM.find({guild:guild})
    .then(queue=>
        {
            if(queue.length>0)
            {
                var i = 0;
                do
                {
                    if(i<queue.length)
                    {
                      
                        console.log('WORKER:',name,'- Music: ','Queue Size:', queue.length,'Queueing: Index:',i, 'Songname:', queue[i].songname);
                        try 
                        {
                      
                            play(queue[i].voice, queue[i].songURL, queue[i].id, queue[i].songname, queue[i].songtime, queue[i].textchannel);
                        } catch (error) 
                        {
                            console.log('WORKER:',name,'- Music: Error: Couldn`t connect to the voice channel.', error);
                        }
                        i=queue.length;
                    }
                    i++;
                    //console.log('Worker:', name, '- I, before condition check:',i, 'length:', queue.length,'condition:', i<queue.length);
                }while((i<queue.length)==true)
            }
            else
            {
                setTimeout(() => 
                {
                    music();    
                }, 1500);
            }
        })
}
/**
  This is an asynchronous function that listens for commands in a guild and performs the appropriate action.
  The function takes two parameters: a player and a connection. The player can be paused, resumed, or stopped,
  and the queue can be skipped or displayed. The function runs continuously, checking for new commands and
  performing the appropriate actions. 
  @param {AudioPlayer} player 
  @param {VoiceConnection} connection 
 */
async function commandListener(player, connection)
{
    const command = await Command.findOne({guild:guild})
    if(!command) return commandListener(player, connector);
    switch (command.command) {
        case 'leave':
            player.stop();
            removeQueue();
            break;
        case 'pause':
            player.pause();
            break;
        case 'resume':
            
            break;
    
        case 'skip':
            removeFromQueue(currentID, player, connection);
            break;
        case 'queue':
            
            break;
    
        default:
            break;
    }
    Command.findByIdAndDelete(command._id)
    .then(() => commandListener(player, connection))

}
function removeQueue()
{
    QueueM.deleteMany({guild:guild});
}
/**
 * 
 * @param {String} voiceID - Voice Channel ID
 * @param {String} songURL - Song URL
 * @param {String} id  - MongoDB ID
 * @param {String} songname - Song Name
 * @param {String} songtime - Song play time
 */
async function play ( voiceID, songURL, id, songname, songtime, text)
{
    currentID = id;
    try
    {
        console.log('Worker:', name ,'- Play: ', voiceID, 'ytID',songURL.split('=')[1],id, songname, songtime, text)
        const connection = joinVoiceChannel({
            channelId: voiceID,
            guildId: guild,
            adapterCreator: bot.guilds.cache.get(guild).voiceAdapterCreator,
        });
        const player = createAudioPlayer({
            behaviors: {
                noSubscriber: NoSubscriberBehavior.Stop,
            }
        });
        connection.on(VoiceConnectionStatus.Ready, () => 
        {
            const resource = createAudioResource(ytdl(songURL,{ filter: 'audioonly', dlChunkSize: 0 }))
            player.play(resource);
            connection.subscribe(player);
            commandListener( player, connection);
            player.on(AudioPlayerStatus.Idle, ()=>
            {
                if(!Pause)
                {
                    removeFromQueue(currentID, player, connection);
                }
            })
        });
    }
    catch (error)
    {
        console.log('Worker:', name ,'- Music: Play: Error: ', error)
    }
}
function playNext(player, voiceID, songURL, id, songname, songtime, text)
{
    currentID = id;
    console.log('Worker:', name ,'- Play: ', voiceID, 'ytID',songURL.split('=')[1],id, songname, songtime, text)
    const resource = createAudioResource(ytdl(songURL,{ filter: 'audioonly', dlChunkSize: 0 }))
    player.play(resource);
}
async function removeFromQueue(id, player, connection)
{
    console.log('Music: Find and Remove: id: ', id);
    QueueM.findByIdAndRemove(id, (err)=>
    {
        if(!err)
        {
            console.log('Worker:',name,'- Music: Song removed!') 
            QueueM.findOne({guild:guild}, (err, next)=>
            {
                if(err) return console.log('Worker:', name, '- Error:', err);
                //console.log('Worker:',name,'- Music: More in queue?', next);
                if(next)
                {
                    console.log('Worker:',name,'- Music: RFQ: Playing next. Next:', next.songname, next.songtime);
                    playNext(player, next.voice, next.songURL, next.id, next.songname, next.songtime, next.textchannel);
                }
                else
                {
                    console.log('Worker', name, ' - Music: RFQ: No more in Q, restarting.');
                    connection.destroy();
                    music();
                }
            })
        } 
        else
        {
            console.log('Worker:',name,'- Music: Failed to remove song from queue!', err)
            music();
        }
    })
   
}