const { SlashCommandBuilder } = require('@discordjs/builders');
const { parse } = require("csv-parse/sync");
const fs = require('node:fs');
const noblox = require("noblox.js");
const axios = require("axios").default;
const gameDict = {
    bhop: "files/bhop_submissions.csv",
    surf: "files/surf_submissions.csv",
    deathrun: "files/deathrun_submissions.csv",
    flytrials: "files/flytrials_submissions.csv",
};
var commandChoices = [];
for (const [game, file] of Object.entries(gameDict)) {
    commandChoices.push({name: game, value: game})
}

async function robloxUserFromDiscord(id) {
    if (isNaN(id)) return undefined;
    try  {
        const res =  await axios.get(`https://api.fiveman1.net/v1/users/${id}`);
        return res.data.result.robloxId;
    } catch (error) {
        return undefined;
    }
}

async function robloxUsernameFromId(id) {
    if (isNaN(id)) return undefined;
    try  {
        const res =  await axios.get(`https://users.roblox.com/v1/users/${id}`);
        return res.data.name;
    } catch (error) {
        return undefined;
    }
}

async function execute(interaction) {
    const userId = await robloxUserFromDiscord(interaction.user.id);
    if (!userId) {
        const msg = "You don't have a Roblox account linked with your Discord account. Use !link with rbhop dog to link your account.";
        await interaction.reply({content: msg, ephemeral: true});
        return;
    }
    const game = interaction.options.getString("game");

    let fname = gameDict[game];
    if (fname === undefined) {
        await interaction.reply({content: "Invalid game specified!", ephemeral: true});
        return;
    }

    if (!fs.existsSync(fname)) {
        fs.writeFileSync(fname, "id,timestamp\n");
    }

    const id = interaction.options.getInteger("asset_id");
    try {
        const info = await getProductInfo(id);
        if (info.AssetTypeId != 10) {
            await interaction.reply({content: `(id: ${id}) is not a valid model ID.`, ephemeral: true});
            return;
        }
        if (info.Creator.Id != userId) {
            const assetUsername = await robloxUsernameFromId(info.Creator.Id);
            const interactionUsername = await robloxUsernameFromId(userId);
            const msg = `The account linked to your Discord (${interactionUsername}) is not the owner of this model (${assetUsername}), so you cannot submit it.`
            await interaction.reply({content: msg, ephemeral: true});
            return
        }
    } catch (error) {
        console.log(error);
        await interaction.reply({content: `There is a problem with this asset ID (id: ${id}).`, ephemeral: true});
        return;
    }

    const csv = fs.readFileSync(fname);
    const records = parse(csv, {delimiter: ',', fromLine: 2});

    let s = "id,timestamp\n";
    for (let record of records) {
        const rid = record[0];
        const rtimestamp = record[1];
        if (id == rid) {
            await interaction.reply({content: `Tried to submit map (id: ${id}) that already exists!`, ephemeral: true});
            return;
        }
        s += `${rid},${rtimestamp}\n`;
    }

    const unix = Math.round(+new Date()/1000);
    s += `${id},${unix}\n`;

    fs.writeFileSync(fname, s);

    await interaction.reply(`Map (id: ${id}) successfully submitted.`);
    
}

module.exports = {
	data: new SlashCommandBuilder()
		.setName('submit')
		.setDescription('Submit your map')
        .addStringOption(option =>
            option.setName("game")
            .setDescription("Select the maptest game")
            .setRequired(true)
            .addChoices(commandChoices))
        .addIntegerOption(option =>
            option.setName("asset_id")
            .setDescription("The asset ID of the model")
            .setRequired(true))
        ,
	execute
};

function getProductInfo (asset) {
    return new Promise(async (resolve, reject) => {
        const httpOpt = {
            url: `//economy.roblox.com/v2/assets/${asset}/details`,
            options: {
                resolveWithFullResponse: true,
                method: 'GET'
            }
        }
        try {
            const res = await noblox.http(httpOpt);
            if (res.statusCode === 200) {
                resolve(JSON.parse(res.body));
            } else {
                // Sourced from: https://stackoverflow.com/a/32278428
                const isAnObject = (val_1) => !!(val_1 instanceof Array || val_1 instanceof Object);

                const body = isAnObject(res.body) ? JSON.parse(res.body) : {};
                if (body.errors && body.errors.length > 0) {
                    const errors = body.errors.map((e) => {
                        return e.message;
                    });
                    reject(new Error(`${res.statusCode} ${errors.join(', ')}`));
                } else {
                    reject(new Error(`${res.statusCode} ${res.body}`));
                }
            }
        } catch (error) {
            return reject(error);
        }
    })
}