const { SlashCommandBuilder } = require('@discordjs/builders');
let birthdayJSON = require('../database/birthdays.json');
const { MessageEmbed } = require('discord.js');
const help = require('../helpFunctions.js');
const PATH = './database/birthdays.json';

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 new birthday entry to database')
                .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('delete')
                .setDescription('Deletes birthday entry')
                .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 person')
                        .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 person')
                        .addUserOption(option => option.setName('user')
                            .setDescription('Select a user')
                            .setRequired(true))
                        .addStringOption(option =>
                            option.setName('nickname')
                            .setDescription('Nickname of birthday person (can be empty to remove)')))),
    async execute(interaction) {
        const error = catchErrors(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(addBirthday(interaction.options));
                    break;
                case 'delete':
                    await interaction.reply(deleteBirthday(interaction.options));
                    break;
                case 'check':
                    await interaction.reply({ embeds: [checkBirthday(interaction)] });
                    break;
            }
        }
        else {
            switch (subcommand) {
                case 'date':
                    await interaction.reply(changeDate(interaction.options));
                    break;
                case 'nickname':
                    await interaction.reply(changeNickname(interaction.options));
                    break;
            }
        }
    },
};

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

    let nickname;
    try {
        nickname = options.getString('nickname');
    }
    catch {
        nickname = '';
    }

    for (let i = 0; i < birthdayJSON.length; i++) {
        const currDay = birthdayJSON[i].day;
        const currMonth = birthdayJSON[i].month;
        if (birthdayJSON[i].id == userId) {
            return 'This user already exists in database';
        }
        if ((currMonth == newMonth && currDay >= newDay) || currMonth > newMonth) {
            const fstPart = birthdayJSON.slice(0, i);
            const sndPart = birthdayJSON.slice(i);
            fstPart.push({ id: userId, day: newDay, month: newMonth, nickname: nickname });
            birthdayJSON = fstPart.concat(sndPart);
            const error = help.writeToFile(birthdayJSON, PATH);
            if (error != null) {
                return 'There was an error while updating the birthday list';
            }
            return `Successfuly added <@${userId}> to the birthday list`;
        }
    }
    birthdayJSON.push({ id: userId, day: newDay, month: newMonth, nickname: nickname });
    const error = help.writeToFile(birthdayJSON, PATH);
    if (error != null) {
        return 'There was an error while updating the birthday list';
    }
    return `Successfuly added <@${userId}> to the birthday list`;
}

function checkBirthday(interaction) {
    const guildMembers = interaction.guild.members;
    const currentDay = new Date().getDate();
    const currentMonth = new Date().getMonth() + 1;

    const closest = [];
    let closestD;
    let closestM;
    let isFirst = true;

    const rng = help.RNG(6);
    let probably = '';
    if (rng == 1) {
        probably = '(probably)';
    }
    else if (rng == 2) {
        probably = '(or will they?)';
    }

    // get the person with day closest to today date
    for (let i = 0; i < birthdayJSON.length; i++) {
        const birthDay = birthdayJSON[i].day;
        const birthMonth = birthdayJSON[i].month;
        const userId = birthdayJSON[i].id;
        const nick = birthdayJSON[i].nickname;
        // first date that is bigger or equal is the closest
        if ((currentMonth == birthMonth && currentDay <= birthDay) || currentMonth < birthMonth) {
            if (isFirst) {
                isFirst = false;
                closestD = birthDay;
                closestM = birthMonth;
            }
            if (!isFirst) {
                if (closestD == birthDay && closestM == birthMonth) {
                    const result = isInGuild(guildMembers, userId);
                    if (result != undefined) {
                        closest.push(`<@${userId}>    ${nick}`);
                    }
                }
                else {
                    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 the closest is in next year -> closest is the first in list
    closestD = birthdayJSON[0].day;
    closestM = birthdayJSON[0].month;
    // check if there are others with the same date just to be sure
    for (let i = 0; i < birthdayJSON.length; i++) {
        const birthDay = birthdayJSON[i].day;
        const birthMonth = birthdayJSON[i].month;
        const userId = birthdayJSON[i].id;
        const nick = birthdayJSON[i].nickname;

        if (closestD == birthDay && closestM == birthMonth) {
            closest.push(`<@${userId}>    ${nick}`);
        }
        else {
            closest.join('\n');
            const embed = new MessageEmbed()
                .setTitle(`Closest birthday is ${birthDay}. ${birthMonth}.`)
                .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;
}

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

    for (let i = 0; i < birthdayJSON.length; i++) {
        if (birthdayJSON[i].id == userId) {
            const fstPart = birthdayJSON.slice(0, i);
            const sndPart = birthdayJSON.slice(i + 1);
            birthdayJSON = fstPart.concat(sndPart);
            const error = help.writeToFile(birthdayJSON, PATH);
            if (error != null) {
                return 'There was an error while updating birthday list';
            }
            return `Successfuly deleted <@${userId}> from birthday list`;
        }
    }
    return 'There was a problem :c';
}

function catchErrors(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;
}

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

    for (let i = 0; i < birthdayJSON.length; i++) {
        const id = birthdayJSON[i].id;
        if (birthdayJSON[i].id == userId) {
            const day = options.getInteger('day');
            const month = options.getInteger('month');
            const nick = birthdayJSON[i].nickname;
            const prevD = birthdayJSON[i].day;
            const prevM = birthdayJSON[i].month;
            let fstPart = birthdayJSON.slice(0, i);
            let sndPart = birthdayJSON.slice(i + 1);
            birthdayJSON = fstPart.concat(sndPart);

            for (let j = 0; j < birthdayJSON.length; j++) {
                const currDay = birthdayJSON[j].day;
                const currMonth = birthdayJSON[j].month;
                if ((currMonth == month && currDay >= day) || currMonth > month) {
                    fstPart = birthdayJSON.slice(0, j);
                    sndPart = birthdayJSON.slice(j);
                    fstPart.push({ id: id, day: day, month: month, nickname: nick });
                    birthdayJSON = fstPart.concat(sndPart);
                    break;
                }
            }
            const error = help.writeToFile(birthdayJSON, PATH);
            if (error != null) {
                return 'There was an error while updating the birthday list';
            }
            return `Successfuly changed birthday date of <@${userId}> from ${prevD}. ${prevM}. to ${day}. ${month}`;
        }
    }
    return 'There was an error (this user probably isn\'t on birthday list)';
}

function changeNickname(options) {

    const userId = options.getUser('user').id;
    for (let i = 0; i < birthdayJSON.length; i++) {
        if (birthdayJSON[i].id == userId) {
            const prevNick = birthdayJSON[i].nickname;
            const newNick = options.getString('nickname');
            birthdayJSON[i].nickname = newNick;

            const error = help.writeToFile(birthdayJSON, PATH);
            if (error != null) {
                return 'There was an error while updating the birthday list';
            }
            return `Succesfully change nickname of <@${userId}> from ${prevNick} to ${newNick}`;
        }
    }
}

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) {
    (await guildMembers.fetch()).find(user => user.id == userId);
}