// Base requires
const { Client, GatewayIntentBits, Events, ApplicationCommandPermissionType } = require("discord.js")
const fs = require("fs")
const { type } = require("os")

// TOKEN is stored in the .env file (node --env-file=.env index.js)
const token = process.env.TOKEN

// Creating a client
const client = new Client({
    intents: [
        GatewayIntentBits.GuildMembers, 
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent, 
        GatewayIntentBits.GuildVoiceStates, 
        GatewayIntentBits.DirectMessages,
        GatewayIntentBits.Guilds
    ]
    }
)

// Exporting the client for event and command files to use it
module.exports.client = client
module.exports.dir = __dirname

// Requiring all the event files.
fs.readdir(`${__dirname}/events/`, (err, files) => {
    files.forEach(file => {
        if (!file.endsWith(".js")) {
            return
        }
        require(`${__dirname}/events/${file}`)
    })
})

// Command list
const commands = []

// Requiring the command files
fs.readdir(`${__dirname}/commands`, (err, files) => {
    files.forEach(file => {
        if (!file.endsWith("js")) {
            return
        }

        const commandList = require(`${__dirname}/commands/${file}`).commands

        commands.push(commandList)
    })
})

// Function to get the command with that name
function getCommand(commandName) {
    var command;
    commands.forEach(commandList => {
        commandList.forEach(commandArray => {
            if (commandArray.name == commandName) {
                command = commandArray
            } 
        })
    })
    return command;
}

// Parsing commands on message create
client.on("messageCreate", async (message) => {
    var content = message.content

    // Continue only if starts with the prefix
    if (!content.startsWith('!')) {
        return
    }

    // Need to remove the prefix after the check
    content = content.slice(1, content.length)

    // Splitting the message into command and args
    const parsed = content.split(" ")

    // Getting command name
    const command = parsed[0]

    // Actual command
    const realCommand = getCommand(command)


    // Only continue if command exists
    if (!realCommand) {
        return
    }

    if (!realCommand.arguments) {
        // No arguments so no need to parse them
        // Run command with no args
        if (realCommand.run.constructor.name == "AsyncFunction"){
            await realCommand.run(message)
        } else {
            realCommand.run(message)
        }
    } else {
        // Removing command from the args
        const args = parsed.filter(value => {return value != command})

        if (args.length < realCommand.arguments.length) {
            const missing = realCommand.arguments.slice(realCommand.arguments.length - args.length)
            missing.forEach((argument, index) => {
                if (typeof argument == "object" || typeof argument == "function") {
                        missing[index] = argument.name
                    }
                })

            return message.reply(`Missing arguments ${missing}`)
        }
        
        var wrongArg = false

        // Parsing
        args.forEach((arg, index) => {
            if (arg.startsWith("<@")) {
                // User, lets get the id
                const id = arg.slice(2, arg.length - 1)

                const member = message.guild.members.cache.find(member => member.user.id == id)

                if (member) {
                    args[index] = member
                }
            } else if (arg.startsWith("<#")) {
                // Channel, lets get the id
                const id = arg.slice(2, arg.length - 1)

                const channel = message.guild.channels.cache.find(channel => channel.id == id)

                if (channel) {
                    args[index] = channel
                }
            }

            // Checks if the type is wrong
            if (typeof args[index] != typeof realCommand.arguments[index] && !(args[index] instanceof realCommand.arguments[index])) {
                wrongArg = true // So it cant continue
                return message.reply(`Argument ${index} supposed to be ${realCommand.arguments[index].name}`)
            }
        })

        if (wrongArg) {
            return
        }

        // Run command
        if (realCommand.run.constructor.name == "AsyncFunction"){
            await realCommand.run(message, ...args)
        } else {
            realCommand.run(message, ...args)
        }
        //...args passes it as separate args rather than an Array
        }
    }
)

/*

Command preset:

module.exports.commands = [ //module.exports.commands exports ONLY COMMANDLISTS.
    {
        name: "name", // !name

        description: "description", // (opt)

        run: (ctx, link, member, num) => {}, // run function

        hide: false, // hide from !help or not

        arguments: ["link", GuildMember, 1] // argument types,
    }
]

*/

// After getting the command list we can export it
module.exports.commands = commands

// Time to login
client.login(token)