import { createCommand } from "@shared/lib/utils";
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
import { UpdateService } from "@shared/modules/admin/update.service";
import {
    getCheckingEmbed,
    getNoUpdatesEmbed,
    getUpdatesAvailableMessage,
    getPreparingEmbed,
    getUpdatingEmbed,
    getCancelledEmbed,
    getTimeoutEmbed,
    getErrorEmbed,
    getRollbackSuccessEmbed,
    getRollbackFailedEmbed
} from "@/modules/admin/update.view";

export const update = createCommand({
    data: new SlashCommandBuilder()
        .setName("update")
        .setDescription("Check for updates and restart the bot")
        .addSubcommand(sub =>
            sub.setName("check")
                .setDescription("Check for and apply available updates")
                .addBooleanOption(option =>
                    option.setName("force")
                        .setDescription("Force update even if no changes detected")
                        .setRequired(false)
                )
        )
        .addSubcommand(sub =>
            sub.setName("rollback")
                .setDescription("Rollback to the previous version")
        )
        .setDefaultMemberPermissions(PermissionFlagsBits.Administrator),

    execute: async (interaction) => {
        const subcommand = interaction.options.getSubcommand();

        if (subcommand === "rollback") {
            await handleRollback(interaction);
        } else {
            await handleUpdate(interaction);
        }
    }
});

async function handleUpdate(interaction: any) {
    await interaction.deferReply({ flags: MessageFlags.Ephemeral });
    const force = interaction.options.getBoolean("force") || false;

    try {
        // 1. Check for updates
        await interaction.editReply({ embeds: [getCheckingEmbed()] });
        const updateInfo = await UpdateService.checkForUpdates();

        if (!updateInfo.hasUpdates && !force) {
            await interaction.editReply({
                embeds: [getNoUpdatesEmbed(updateInfo.currentCommit)]
            });
            return;
        }

        // 2. Analyze requirements
        const requirements = await UpdateService.checkUpdateRequirements(updateInfo.branch);
        const categories = UpdateService.categorizeChanges(requirements.changedFiles);

        // 3. Show confirmation with details
        const { embeds, components } = getUpdatesAvailableMessage(
            updateInfo,
            requirements,
            categories,
            force
        );
        const response = await interaction.editReply({ embeds, components });

        // 4. Wait for confirmation
        try {
            const confirmation = await response.awaitMessageComponent({
                filter: (i: any) => i.user.id === interaction.user.id,
                componentType: ComponentType.Button,
                time: 30000
            });

            if (confirmation.customId === "confirm_update") {
                await confirmation.update({
                    embeds: [getPreparingEmbed()],
                    components: []
                });

                // 5. Save rollback point
                const previousCommit = await UpdateService.saveRollbackPoint();

                // 6. Prepare restart context
                await UpdateService.prepareRestartContext({
                    channelId: interaction.channelId,
                    userId: interaction.user.id,
                    timestamp: Date.now(),
                    runMigrations: requirements.needsMigrations,
                    installDependencies: requirements.needsRootInstall || requirements.needsWebInstall,
                    buildWebAssets: requirements.needsWebBuild,
                    previousCommit: previousCommit.substring(0, 7),
                    newCommit: updateInfo.latestCommit
                });

                // 7. Show updating status
                await interaction.editReply({
                    embeds: [getUpdatingEmbed(requirements)]
                });

                // 8. Perform update
                await UpdateService.performUpdate(updateInfo.branch);

                // 9. Trigger restart
                await UpdateService.triggerRestart();

            } else {
                await confirmation.update({
                    embeds: [getCancelledEmbed()],
                    components: []
                });
            }

        } catch (e) {
            if (e instanceof Error && e.message.includes("time")) {
                await interaction.editReply({
                    embeds: [getTimeoutEmbed()],
                    components: []
                });
            } else {
                throw e;
            }
        }

    } catch (error) {
        console.error("Update failed:", error);
        await interaction.editReply({
            embeds: [getErrorEmbed(error)],
            components: []
        });
    }
}

async function handleRollback(interaction: any) {
    await interaction.deferReply({ flags: MessageFlags.Ephemeral });

    try {
        const hasRollback = await UpdateService.hasRollbackPoint();

        if (!hasRollback) {
            await interaction.editReply({
                embeds: [getRollbackFailedEmbed("No rollback point available. Rollback is only possible after a recent update.")]
            });
            return;
        }

        const result = await UpdateService.rollback();

        if (result.success) {
            await interaction.editReply({
                embeds: [getRollbackSuccessEmbed(result.message.split(" ").pop() || "unknown")]
            });

            // Restart after rollback
            setTimeout(() => UpdateService.triggerRestart(), 1000);
        } else {
            await interaction.editReply({
                embeds: [getRollbackFailedEmbed(result.message)]
            });
        }

    } catch (error) {
        console.error("Rollback failed:", error);
        await interaction.editReply({
            embeds: [getErrorEmbed(error)]
        });
    }
}
