const { SlashCommandBuilder, ComponentType, ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder, TextInputStyle, TextInputBuilder } = require("discord.js");
const { Character, Group, RecordHistory } = require("../models");
const { UserUtils, GeneralUtils } = require("../util");
const axios = require("axios");
const fs = require("fs");

module.exports = {
    data: new SlashCommandBuilder()
            .setName("edit")
            .setDescription("Admin command to edit records")
            .addStringOption((option) =>
                option
                    .setName("type")
                    .setDescription("The thing to edit")
                    .setRequired(true)
                    .addChoices(
                        { name: 'character', value: 'character' },
                        { name: 'group', value: 'group' }
                    )
                )
            .addStringOption((option) =>
                option
                    .setName("id")
                    .setDescription("Thing ID")
                    .setRequired(true)
                )
            .addAttachmentOption((option) =>
                option
                    .setName("attachement")
                    .setDescription("Attachement to be used")
                    .setRequired(false)
                ),
    permissionLevel: 2,
    async execute(interaction, type=undefined, id=undefined) {
        await interaction.deferReply();
        let user = await UserUtils.getUserByDiscordId(interaction.member.id);

        let options = [];
        let record;
        switch (interaction.options?.getString("type") ?? type) {
        case "character":
            record = await Character.findByPk(interaction.options?.getString("id") ?? id, { include: Group });
            if (!record) {
                interaction.editReply({ content: "Character not found" });
                return;
            }
            options = [ "name", "description", "imageIdentifier" ];
            break;
        case "group":
            options = [ "name", "description" ];
            break;
        default:
            interaction.reply({
                content: `Your permission level is ${await UserUtils.getPermissionLevel(interaction.member)}`,
                ephemeral: false
            });
            break;
        }

        //row of button components to select what property to edit
        const row = new ActionRowBuilder();
        for (option of options) {
            row.addComponents(
                new ButtonBuilder()
                    .setLabel(`Edit ${option}`)
                    .setCustomId(`${option}-${interaction.id}`)
                    .setStyle(ButtonStyle.Primary)
            );
        }

        //show buttons
        let message = await interaction.editReply({ content: `You've selected [${record.identifier ?? record.id}] ${record.name}`, components: [row], fetchReply: true });

        //filter only events from the user who triggered the command
        const filter = (m) => m.author.id === interaction.author.id && m.customId.endsWith(interaction.id);
        const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 65000 })

        collector.on('collect', async (m) => {
            let option = m.customId.split("-")[0];
            switch (option) {
                case 'name':
                    let newName = await this.openStringModal(m, `Edit ${option} for ${record.name}`, "", "name-"+interaction.id);
                    if (newName) {
                        await this.updateRecord(user, record, option, newName.value);
                    }
                    break;
                case 'description':
                    let newDesc = await this.openParagraphModal(m, `Edit ${option} for ${record.name}`, "", "desc-"+interaction.id);
                    if (newDesc) {
                        await this.updateRecord(user, record, option, newDesc.value);
                    }
                    break;
                case 'imageIdentifier':
                    m.deferReply();
                    let allowedContentTypes = [ "image/png", "image/jpeg", "image/gif" ];
                    let image = interaction.options.getAttachment("attachement");
                    if (!image) {
                        await m.reply({ content: "You must attach an image", ephemeral: true });
                        return;
                    }
                    if (!allowedContentTypes.includes(image.contentType)) {
                        await m.reply({ content: "An invalid image has been attached", ephemeral: true });
                        return;
                    }
                    let identifier = `${record.Group.name.replace(" ", "_")}/${image.name}`;
                    let path = `/app/assets/cards/${identifier}`;
                    await this.downloadImage(image.attachment, path);
                    await this.updateRecord(user, record, option, identifier);
                    m.editReply({ content: "Attached image has been applied" });
                    break;
                default:
                    m.reply({ content: "Invalid selection" });
                    break;
            }
        });
    },
    async downloadImage(url, path) {
        let imageBuffer = await axios.get(url, { responseType: 'arraybuffer' });
        fs.writeFileSync(path, imageBuffer.data);
    },

    async openParagraphModal(interaction, title, placeholder, persistentId) {
        const customId = 'paragraphModal-' + persistentId;
        const modal = new ModalBuilder()
            .setCustomId(customId)
            .setTitle(title.substr(0, 44));

        let row = new ActionRowBuilder();
        let textInput = new TextInputBuilder()
            .setCustomId(`input-${persistentId}`)
            .setLabel("Enter new value")
            .setStyle(TextInputStyle.Paragraph)
            .setRequired(false)
            .setMaxLength(255)
            .setPlaceholder(placeholder ?? "We've been trying to get a hold of you about your car's extended warranty.");
        row.addComponents(textInput);
        modal.addComponents(row);

        let message = await interaction.showModal(modal);

        let submitted = await interaction.awaitModalSubmit({
            time: 300000,
            filter: i => i.user.id === interaction.user.id,
          }).catch(error => {
            console.error(error)
            return null
          })
          
          if (submitted && submitted.customId == customId) {
            await submitted.deferReply();
            await submitted.editReply({ content: `You submitted: ${submitted.fields.getTextInputValue(`input-${persistentId}`)}`})
            return { i: submitted, value: submitted.fields.getTextInputValue(`input-${persistentId}`)};
          }
    },
    async openStringModal(interaction, title, placeholder, persistentId) {
        const customId = 'textModal-' + persistentId;
        const modal = new ModalBuilder()
            .setCustomId(customId)
            .setTitle(title.substr(0, 44));

        let row = new ActionRowBuilder();
        let textInput = new TextInputBuilder()
            .setCustomId(`input-${persistentId}`)
            .setLabel("Enter new value")
            .setStyle(TextInputStyle.Short)
            .setRequired(false)
            .setMaxLength(255)
            .setPlaceholder(placeholder ?? "");
        row.addComponents(textInput);
        modal.addComponents(row);

        let message = await interaction.showModal(modal);

        let submitted = await interaction.awaitModalSubmit({
            time: 300000,
            filter: i => i.user.id === interaction.user.id,
          }).catch(error => {
            console.error(error)
            return null
          })

          if (submitted && submitted.customId == customId) {
            await submitted.deferReply();
            await submitted.editReply({ content: `You submitted: ${submitted.fields.getTextInputValue(`input-${persistentId}`)}` });
            return { i: submitted, value: submitted.fields.getTextInputValue(`input-${persistentId}`)};
          }
    },
    async updateRecord(user, record, property, value) {
        let history = {
            affectedId: record.id,
            userId: user.id,
            type: record.constructor.name,
            property: property,
            oldValue: record[property],
            newValue: value
        }
        record[property] = value;
        await record.save();
        await RecordHistory.create(history);
    }
}