import { SlashCommandBuilder, EmbedBuilder, inlineCode, PermissionFlagsBits, MessageFlags } from "discord.js"
import type { ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData, Locale } from "discord.js"
import * as AMP from "@/utils/amp"
import type { Host, Instance, InstanceFields, InstanceResult, LoginSuccessData } from "@/types/amp"
import type { ReturnMsgData } from "@/types"
import type { GuildAmp } from "@/types/schemas"
import dbGuild from "@/schemas/guild"
import { t } from "@/utils/i18n"

function returnMsg(result: ReturnMsgData, locale: Locale) {
	if (result.status === "fail") return `${t(locale, "common.failed")}\n${inlineCode(`${result.Title}: ${result.Message}`)}`
	if (result.status === "error") return `${t(locale, "common.error_occurred")}\n${inlineCode(`${result.error_code}`)}`
}

export const data = new SlashCommandBuilder()
	.setName("amp")
	.setDescription("Access my AMP gaming panel")
	.setDescriptionLocalizations({ fr: "Accède à mon panel de jeu AMP" })
	.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
	.addSubcommand(subcommand => subcommand
		.setName("login")
		.setDescription("Log in before performing another command")
		.setNameLocalizations({ fr: "connexion" })
		.setDescriptionLocalizations({ fr: "Connectez-vous avant d'effectuer une autre commande" })
		.addStringOption(option => option
			.setName("username")
			.setDescription("Username")
			.setNameLocalizations({ fr: "nom_utilisateur" })
			.setDescriptionLocalizations({ fr: "Nom d'utilisateur" })
			.setRequired(true)
		)
		.addStringOption(option => option
			.setName("password")
			.setDescription("Password")
			.setNameLocalizations({ fr: "mot_de_passe" })
			.setDescriptionLocalizations({ fr: "Mot de passe" })
			.setRequired(true)
		)
		.addBooleanOption(option => option
			.setName("remember")
			.setDescription("Remember credentials")
			.setNameLocalizations({ fr: "memoriser" })
			.setDescriptionLocalizations({ fr: "Mémoriser les identifiants" })
			.setRequired(true)
		)
		.addStringOption(option => option
			.setName("otp")
			.setDescription("Two-factor authentication code")
			.setNameLocalizations({ fr: "otp" })
			.setDescriptionLocalizations({ fr: "Code d'authentification à 2 facteurs" })
		)
	)
	.addSubcommandGroup(subcommandgroup => subcommandgroup
		.setName("instances")
		.setDescription("Interact with AMP instances")
		.setNameLocalizations({ fr: "instances" })
		.setDescriptionLocalizations({ fr: "Intéragir avec les instances AMP" })
		.addSubcommand(subcommand => subcommand
			.setName("list")
			.setDescription("List all available instances")
			.setNameLocalizations({ fr: "liste" })
			.setDescriptionLocalizations({ fr: "Lister toutes les instances disponibles" })
		)
		.addSubcommand(subcommand => subcommand
			.setName("manage")
			.setDescription("Manage an instance")
			.setNameLocalizations({ fr: "gerer" })
			.setDescriptionLocalizations({ fr: "Gérer une instance" })
			.addStringOption(option => option
				.setName("instance")
				.setDescription("Instance name")
				.setNameLocalizations({ fr: "instance" })
				.setDescriptionLocalizations({ fr: "Nom de l'instance" })
				.setRequired(true)
				.setAutocomplete(true)
			)
		)
		.addSubcommand(subcommand => subcommand
			.setName("restart")
			.setDescription("Restart an instance")
			.setNameLocalizations({ fr: "redemarrer" })
			.setDescriptionLocalizations({ fr: "Redémarrer une instance" })
			.addStringOption(option => option
				.setName("name")
				.setDescription("Instance name")
				.setNameLocalizations({ fr: "nom" })
				.setDescriptionLocalizations({ fr: "Nom de l'instance" })
				.setRequired(true)
			)
		)
	)

export async function execute(interaction: ChatInputCommandInteraction) {
	const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id })
	if (!guildProfile) return interaction.reply({ content: t(interaction.locale, "common.database_not_found"), flags: MessageFlags.Ephemeral })

	const dbData = guildProfile.get("guildAmp") as GuildAmp
	if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "amp.module_disabled"), flags: MessageFlags.Ephemeral })

	const host = dbData.host
	if (!host) return interaction.reply({ content: t(interaction.locale, "amp.host_not_configured"), flags: MessageFlags.Ephemeral })

	let username = dbData.username
	let sessionID = dbData.sessionID
	let rememberMeToken = dbData.rememberMeToken

	const subcommandGroup = interaction.options.getSubcommandGroup(false)
	const subcommand = interaction.options.getSubcommand(true)
	if (subcommand == "login") {
		// Get a SessionID and a RememberMeToken if wanted
		await interaction.deferReply({ flags: MessageFlags.Ephemeral })

		const details = {
			username: interaction.options.getString("username", true),
			password: interaction.options.getString("password", true),
			token: interaction.options.getString("otp") ?? "",
			rememberMe: interaction.options.getBoolean("remember", true)
		}

		const result = await AMP.Core.Login(host, details)
		if (result.status !== "success") return interaction.followUp({ content: returnMsg(result, interaction.locale), flags: MessageFlags.Ephemeral })

		const loginData = result.data as LoginSuccessData
		username = dbData.username = loginData.userInfo.Username
		sessionID = dbData.sessionID = loginData.sessionID
		rememberMeToken = dbData.rememberMeToken = loginData.rememberMeToken

		guildProfile.set("guildAmp", dbData)
		guildProfile.markModified("guildAmp")
		await guildProfile.save().catch(console.error)

		return interaction.followUp({ content: t(interaction.locale, "amp.logged_in", { username }), flags: MessageFlags.Ephemeral })
	}
	await interaction.deferReply()

	// Check if the SessionID is still valid
	if (!sessionID) return interaction.followUp({ content: t(interaction.locale, "amp.login_required"), flags: MessageFlags.Ephemeral })

	const checkResult = await AMP.CheckSession(host, sessionID)
	if (checkResult.status === "fail") {
		if (rememberMeToken && username) {
			// Refresh the SessionID if the RememberMeToken is available
			const details = { username, password: "", token: rememberMeToken, rememberMe: true }
			const loginResult = await AMP.Core.Login(host, details)
			if (loginResult.status !== "success") return interaction.followUp({ content: returnMsg(loginResult, interaction.locale), flags: MessageFlags.Ephemeral })

			const loginData = loginResult.data as LoginSuccessData
			sessionID = loginData.sessionID
		}
		else return interaction.followUp({ content: t(interaction.locale, "amp.login_required"), flags: MessageFlags.Ephemeral })
	}
	else if (checkResult.status === "error") return interaction.followUp({ content: returnMsg(checkResult, interaction.locale), flags: MessageFlags.Ephemeral })

	if (subcommandGroup == "instances") {
		if (subcommand == "list") {
			const result = (await AMP.ADSModule.GetInstances(host, sessionID)) as InstanceResult
			if (result.status !== "success") return interaction.followUp({ content: returnMsg(result, interaction.locale), flags: MessageFlags.Ephemeral })

			await interaction.followUp({ content: t(interaction.locale, "amp.hosts_found", { count: result.data.length }) })
			await Promise.all(result.data.map(async host => {
				const fields = [] as InstanceFields[]
				host.AvailableInstances.forEach((instance: Instance) => {
					fields.push({ name: instance.FriendlyName, value: `**${t(interaction.locale, "amp.running")}:** ${instance.Running}\n**${t(interaction.locale, "amp.port")}:** ${instance.Port}\n**${t(interaction.locale, "amp.module")}:** ${instance.Module}`, inline: true })
				})
				const embed = new EmbedBuilder()
					.setTitle(host.FriendlyName)
					.setDescription(t(interaction.locale, "amp.instance_list", { count: host.AvailableInstances.length }))
					.setColor(interaction.guild?.members.me?.displayColor ?? "#ffc370")
					.setTimestamp()
					.setFields(fields)
				return interaction.followUp({ embeds: [embed] })
			}))
		}
		else if (subcommand == "manage") {
			const instanceID = interaction.options.getString("instance", true)

			const manageResult = await AMP.ADSModule.ManageInstance(host, sessionID, instanceID)
			if (manageResult.status !== "success") return interaction.followUp({ content: returnMsg(manageResult, interaction.locale), flags: MessageFlags.Ephemeral })

			const serversResult = await AMP.ADSModule.Servers(host, sessionID, instanceID)
			if (serversResult.status !== "success") return interaction.followUp({ content: returnMsg(serversResult, interaction.locale), flags: MessageFlags.Ephemeral })

			return interaction.followUp({ content: t(interaction.locale, "amp.manage_success") })
		}
		else if (subcommand == "restart") {
			const query = interaction.options.getString("name", true)

			const restartResult = await AMP.ADSModule.RestartInstance(host, sessionID, query)
			if (restartResult.status !== "success") return interaction.followUp({ content: returnMsg(restartResult, interaction.locale), flags: MessageFlags.Ephemeral })

			return interaction.followUp({ content: t(interaction.locale, "amp.restart_success") })
		}
	}
}
export async function autocompleteRun(interaction: AutocompleteInteraction) {
	const query = interaction.options.getString("instance", true)

	const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id })
	if (!guildProfile) return interaction.respond([])

	const dbData = guildProfile.get("guildAmp") as GuildAmp
	if (!dbData.enabled) return interaction.respond([])

	const host = dbData.host
	if (!host) return interaction.respond([])

	let sessionID = dbData.sessionID
	if (!sessionID) return interaction.respond([])

	const username = dbData.username
	const rememberMeToken = dbData.rememberMeToken

	const checkResult = await AMP.CheckSession(host, sessionID)
	if (checkResult.status === "fail") {
		if (rememberMeToken && username) {
			// Refresh the SessionID if the RememberMeToken is available
			const details = { username, password: "", token: rememberMeToken, rememberMe: true }
			const loginResult = await AMP.Core.Login(host, details)
			if (loginResult.status !== "success") return interaction.respond([])

			const loginData = loginResult.data as LoginSuccessData
			sessionID = loginData.sessionID
		}
		else return interaction.respond([])
	}
	else if (checkResult.status === "error") return interaction.respond([])

	const instancesResult = (await AMP.ADSModule.GetInstances(host, sessionID)) as InstanceResult
	if (instancesResult.status !== "success") return interaction.respond([])

	const choices: ApplicationCommandOptionChoiceData[] = []
	const hosts = instancesResult.data as Host[]
	hosts.forEach(host => {
		const instances = host.AvailableInstances
		instances.forEach(instance => {
			if (instance.FriendlyName.includes(query)) choices.push({ name: `${host.FriendlyName} - ${instance.FriendlyName}`, value: instance.InstanceID })
		})
	})

	return interaction.respond(choices)
}
