const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const help = require('../helpFunctions.js');
const bModel = require('../database/birthdaySchema');

module.exports = {
	data: new SlashCommandBuilder()
		.setName('birthday')
		.setDescription('shows birthday')
        .addSubcommand(subcommand =>
            subcommand
                .setName('check')
                .setDescription('Checks who\'s birthday is the closest'))
        .addSubcommand(subcommand =>
            subcommand
                .setName('add')
                .setDescription('Adds user to birthday list')
                .addUserOption(option => option.setName('user')
                    .setDescription('Select a user')
                    .setRequired(true))
                .addIntegerOption(option =>
                    option.setName('day')
                    .setDescription('Day of birth')
                    .setRequired(true))
                .addIntegerOption(option =>
                    option.setName('month')
                    .setDescription('Month of birth')
                    .setRequired(true))
                .addStringOption(option =>
                    option.setName('nickname')
                    .setDescription('Nickname of birthday person')))
        .addSubcommand(subcommand =>
            subcommand
                .setName('remove')
                .setDescription('Removes user from birthday list')
                .addUserOption(option => option.setName('user')
                    .setDescription('Select a user')
                    .setRequired(true)))
        .addSubcommandGroup(subcommandGroup =>
            subcommandGroup
                .setName('change')
                .setDescription('Change the birthday entry')
                .addSubcommand(subcommand =>
                    subcommand
                        .setName('date')
                        .setDescription('Change date of a user')
                        .addUserOption(option => option.setName('user')
                            .setDescription('Select a user')
                            .setRequired(true))
                        .addIntegerOption(option =>
                            option.setName('day')
                            .setDescription('Day of birth')
                            .setRequired(true))
                        .addIntegerOption(option =>
                            option.setName('month')
                            .setDescription('Month of birth')
                            .setRequired(true)))
                .addSubcommand(subcommand =>
                    subcommand
                        .setName('nickname')
                        .setDescription('Change nickname of a user')
                        .addUserOption(option => option.setName('user')
                            .setDescription('Select a user')
                            .setRequired(true))
                        .addStringOption(option =>
                            option.setName('nickname')
                            .setDescription('Nickname of birthday a user (can be empty to remove)')))),
    async execute(interaction) {
        const error = catchDateErrors(interaction.options);
        if (error != null) {
            await interaction.reply(error);
        }
        let subcommandGroup;
        try {
            subcommandGroup = interaction.options.getSubcommandGroup();
        }
        catch {
            subcommandGroup = undefined;
        }
        const subcommand = interaction.options.getSubcommand();
        if (subcommandGroup == undefined) {
            switch (subcommand) {
                case 'add':
                    await interaction.reply(await addBirthday(interaction.options));
                    break;
                case 'remove':
                    await interaction.reply(await removeBirthday(interaction.options));
                    break;
                case 'check':
                    await interaction.reply({ embeds: [await checkBirthday(interaction)] });
                    break;
            }
        }
        else {
            switch (subcommand) {
                case 'date':
                    await interaction.reply(await changeDate(interaction.options));
                    break;
                case 'nickname':
                    await interaction.reply(await changeNickname(interaction.options));
                    break;
            }
        }
    },
};

async function addBirthday(options) {
    const userId = options.getUser('user').id;
    const newDay = options.getInteger('day');
    const newMonth = options.getInteger('month');

    const nickname = options.getString('nickname');

    let error = null;
    try {
        const dbEntry = await bModel.create({
            id: userId,
            day: newDay,
            month: newMonth,
            name: nickname,
        });
        dbEntry.save();
        error = await sortTable();
    }
    catch (err) {
        error = err;
        console.log(err);
    }
    if (error != null) {
        return 'There was an error \n(user is probably already on the birthday list)';
    }
    return `Successfuly added <@${userId}> to the birthday list`;
}

async function checkBirthday(interaction) {
    const currDay = new Date().getDate();
    const currMonth = new Date().getMonth() + 1;


    const query = bModel.find({});
    const result = await query.exec();

    let closestD;
    let closestM;
    const closest = [];
    const guildMembers = interaction.guild.members;
    let isFirst = true;
    for (let i = 0; i < result.length; i++) {
        const birthDay = result[i].day;
        const birthMonth = result[i].month;
        const userId = result[i].id;
        const nick = result[i].nickname;
        if ((currMonth == birthMonth && currDay <= birthDay) || currMonth < birthMonth) {
            if (await isInGuild(guildMembers, userId)) {
                if (isFirst) {
                    closestD = birthDay;
                    closestM = birthMonth;
                    isFirst = false;
                }
                if (!isFirst && (closestD == birthDay && closestM == birthMonth)) {
                    closest.push(`<@${userId}>    ${nick}`);
                }
                else {
                    const probably = getProbably();
                    const personList = closest.join('\n');
                    const embed = new MessageEmbed()
                    .setTitle(`Closest birthday is ${closestD}. ${closestM}.`)
                    .setDescription(`${personList} \n will celebrate... ${probably}`)
                    .setColor(help.randomColor());
                    return embed;
                }
            }
        }
    }

    if (closest.length > 0) {
        const probably = getProbably();
        const personList = closest.join('\n');
        const embed = new MessageEmbed()
            .setTitle(`Closest birthday is ${closestD}. ${closestM}.`)
            .setDescription(`${personList} \n will celebrate... ${probably}`)
            .setColor(help.randomColor());
        return embed;
    }

    // ? if the closest is in next year -> closest is the first in list
    closestD = result[0].day;
    closestM = result[0].month;
    // check if there are others with the same date just to be sure
    for (let i = 0; i < result.length; i++) {
        const birthDay = result[i].day;
        const birthMonth = result[i].month;
        const userId = result[i].id;
        const nick = result[i].nickname;

        if (closestD == birthDay && closestM == birthMonth) {
            closest.push(`<@${userId}>    ${nick}`);
        }
        else {
            const probably = getProbably();
            closest.join('\n');
            const embed = new MessageEmbed()
                .setTitle(`Closest birthday is ${closestD}. ${closestM}.`)
                .setDescription(`${closest} \n will celebrate ${probably}`)
                .setColor(help.randomColor());
                return embed;
        }
    }

    // if noone from server is in the birthday list (and maybe something else)
    const embed = new MessageEmbed()
        .setTitle('Oh no...')
        .setDescription('There was an error');
    return embed;
}

async function removeBirthday(options) {
    const userId = options.getUser('user').id;

    let error = null;
    await bModel.deleteOne({ id: userId }), function(err) {
        if (err) error = err;
    };
    if (error) return 'There was an error';
    return `Successfuly deleted <@${userId}> from birthday list`;
}

function catchDateErrors(options) {
    const month = options.getInteger('month');
    const day = options.getInteger('day');
    if (month == null || day == null) {
        return null;
    }

    if (month > 12 || month < 1) {
        return 'Bruh, "month" kinda poopy';
    }
    if (day > checkMonth(month) || day < 1) {
        return 'Bruh, "day" kinda poopy';
    }
    return null;
}

async function changeDate(options) {
    const userId = options.getUser('user').id;
    const newDay = options.getInteger('day');
    const newMonth = options.getInteger('month');

    try {
        await bModel.findOneAndUpdate({ id: userId }, { $set: { day: newDay, month: newMonth } });
        sortTable();
    }
    catch (err) {
        console.log(err);
        return 'There was an error while updating the birthday list';
    }
    return `Successfuly changed birthday date of <@${userId}> to ${newDay}. ${newMonth}.`;
}

async function changeNickname(options) {
    const userId = options.getUser('user').id;
    let nick = options.getString('nickname');
    if (nick == null) nick = '';
    try {
        await bModel.findOneAndUpdate({ id: userId }, { $set: { nickname: nick } });
    }
    catch {
        return 'There was an error';
    }
    return `Succesfully change nickname of <@${userId}> to ${nick}`;
}

async function sortTable() {
    const query = bModel.find({}).sort({ month: 'asc', day: 'asc' });
    const result = await query.exec();
    let error;
    await bModel.deleteMany({}), function(err) {
        if (err) error = err;
    };

    if (error) return error;

    for (let i = 0; i < result.length; i++) {
        const entry = await bModel.create({
            id: result[i].id,
            day: result[i].day,
            month: result[i].month,
            nickname: result[i].nickname,
        });
        entry.save();
    }

    return null;
}

function getProbably() {
    const rng = help.RNG(6);
    switch (rng) {
        case 0:
            return 'probably';
        case 1:
            return 'or not';
        case 2:
            return 'or will they?';
        case 3:
            return '\n You may be older, but I still love you the same don\'t worry';
        default:
            return '';
    }
}

async function checkMonth(month) {
    switch (month) {
        case 1:
            return 31;
        case 2:
            return 28;
        case 3:
            return 31;
        case 4:
            return 30;
        case 5:
            return 31;
        case 6:
            return 30;
        case 7:
            return 31;
        case 8:
            return 31;
        case 9:
            return 30;
        case 10:
            return 31;
        case 11:
            return 30;
        case 12:
            return 31;
    }
}

async function isInGuild(guildMembers, userId) {
    try {
        await guildMembers.fetch(userId);
    }
    catch {
        return false;
    }
    return true;
}