import { SlashCommandBuilder, ChannelType, MessageFlags, PermissionFlagsBits } from "discord.js"
import type { ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData } from "discord.js"
import { twitchClient, listener, onlineSub, offlineSub, generateTwitchEmbed } from "@/utils/twitch"
import type { GuildTwitch } from "@/types/schemas"
import dbGuild from "@/schemas/guild"
import { t } from "@/utils/i18n"
import { logConsole, logConsoleError } from "@/utils/console"

export const data = new SlashCommandBuilder()
	.setName("twitch")
	.setDescription("Manage streamers notifications")
	.setDescriptionLocalizations({ fr: "Gérer les notifications des streameurs" })
	.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
	.addSubcommand(subcommand => subcommand
		.setName("status")
		.setDescription("Display Twitch module status")
		.setNameLocalizations({ fr: "statut" })
		.setDescriptionLocalizations({ fr: "Afficher le statut du module Twitch" })
	)
	.addSubcommand(subcommand => subcommand
		.setName("channel")
		.setDescription("Set the channel to send notifications")
		.setNameLocalizations({ fr: "canal" })
		.setDescriptionLocalizations({ fr: "Définir le canal pour envoyer les notifications" })
		.addChannelOption(option => option
			.setName("channel")
			.setDescription("The channel to send notifications")
			.setNameLocalizations({ fr: "canal" })
			.setDescriptionLocalizations({ fr: "Le canal pour envoyer les notifications" })
			.setRequired(true)
		)
	)
	.addSubcommandGroup(subcommandgroup => subcommandgroup
		.setName("streamer")
		.setDescription("Manage streamers")
		.setNameLocalizations({ fr: "streameur" })
		.setDescriptionLocalizations({ fr: "Gérer les streameurs" })
		.addSubcommand(subcommand => subcommand
			.setName("list")
			.setDescription("List all streamers")
			.setNameLocalizations({ fr: "liste" })
			.setDescriptionLocalizations({ fr: "Lister tous les streameurs" })
		)
		.addSubcommand(subcommand => subcommand
			.setName("add")
			.setDescription("Add a streamer")
			.setNameLocalizations({ fr: "ajouter" })
			.setDescriptionLocalizations({ fr: "Ajouter un streameur" })
			.addStringOption(option => option
				.setName("username")
				.setDescription("The username of the streamer to add")
				.setNameLocalizations({ fr: "nom_utilisateur" })
				.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à ajouter" })
				.setRequired(true)
				.setAutocomplete(true)
			)
			.addUserOption(option => option
				.setName("member")
				.setDescription("The member on the guild to mention")
				.setNameLocalizations({ fr: "membre" })
				.setDescriptionLocalizations({ fr: "Le membre sur le serveur à mentionner" })
				.setRequired(false)
			)
		)
		.addSubcommand(subcommand => subcommand
			.setName("remove")
			.setDescription("Remove a streamer")
			.setNameLocalizations({ fr: "supprimer" })
			.setDescriptionLocalizations({ fr: "Supprimer un streameur" })
			.addStringOption(option => option
				.setName("username")
				.setDescription("The username of the streamer to remove")
				.setNameLocalizations({ fr: "nom_utilisateur" })
				.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à supprimer" })
				.setRequired(true)
				.setAutocomplete(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("guildTwitch") as GuildTwitch

	const subcommandGroup = interaction.options.getSubcommandGroup(false)
	const subcommand = interaction.options.getSubcommand(true)
	if (subcommand == "status") {
		// Utiliser la fonction utilitaire pour générer l'embed et les composants
		const { embed, components } = generateTwitchEmbed(dbData, interaction.client, interaction.guild?.id ?? "", interaction.locale)

		return interaction.reply({ embeds: [embed], components: components, flags: MessageFlags.Ephemeral })
	}

	if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "twitch.module_disabled_activate"), flags: MessageFlags.Ephemeral })

	if (subcommand == "channel") {
		const channel = interaction.options.getChannel("channel", true)
		if (channel.type !== ChannelType.GuildText && channel.type !== ChannelType.GuildAnnouncement)
			return interaction.reply({ content: t(interaction.locale, "common.invalid_text_channel"), flags: MessageFlags.Ephemeral })

		dbData.channelId = channel.id
		guildProfile.set("guildTwitch", dbData)
		guildProfile.markModified("guildTwitch")
		await guildProfile.save().catch(console.error)

		return interaction.reply({ content: t(interaction.locale, "twitch.notifications_channel_set", { channel: channel.name ?? channel.id }), flags: MessageFlags.Ephemeral })
	}
	else if (subcommandGroup == "streamer") {
		if (!dbData.channelId) return interaction.reply({ content: t(interaction.locale, "twitch.configure_channel_first"), flags: MessageFlags.Ephemeral })

		if (subcommand == "list") {
			if (!dbData.streamers.length) return interaction.reply({ content: t(interaction.locale, "twitch.no_streamers_list"), flags: MessageFlags.Ephemeral })

			const streamers = [] as string[]
			await Promise.all(dbData.streamers.map(async streamer => {
				try {
					const user = await twitchClient.users.getUserById(streamer.twitchUserId)
					if (user) streamers.push(`- ${user.displayName} (${streamer.twitchUserId})`)
					else streamers.push(`- ${t(interaction.locale, "twitch.user_not_found_id", { id: streamer.twitchUserId })}`)
				} catch (error) {
					logConsoleError('twitch', 'user_fetch_error', { id: streamer.twitchUserId }, error as Error)
				}
			}))
			const streamerList = streamers.length > 0 ? streamers.join("\n") : t(interaction.locale, "twitch.no_streamers")

			return interaction.reply({ content: `${t(interaction.locale, "twitch.list.title")}:\n${streamerList}`, flags: MessageFlags.Ephemeral })
		}
		else if (subcommand == "add") {
			const username = interaction.options.getString("username", true)
			const member = interaction.options.getUser("member", false)

			const user = await twitchClient.users.getUserByName(username)
			if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral })

			if (dbData.streamers.some(s => s.twitchUserId === user.id)) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_already_added", { username }), flags: MessageFlags.Ephemeral })
			dbData.streamers.push({ twitchUserId: user.id, discordUserId: member?.id ?? "", messageId: "" })

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

			const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id)
			// eslint-disable-next-line @typescript-eslint/no-misused-promises
			if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.online")) listener.onStreamOnline(user.id, onlineSub)
			// eslint-disable-next-line @typescript-eslint/no-misused-promises
			if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.offline")) listener.onStreamOffline(user.id, offlineSub)

			return interaction.reply({ content: t(interaction.locale, "twitch.streamer_added", { username, id: user.id }), flags: MessageFlags.Ephemeral })
		}
		else if (subcommand == "remove") {
			const username = interaction.options.getString("username", true)

			const user = await twitchClient.users.getUserByName(username)
			if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral })

			const streamerIndex = dbData.streamers.findIndex(s => s.twitchUserId === user.id)
			if (streamerIndex === -1)return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_in_list", { username }), flags: MessageFlags.Ephemeral })

			dbData.streamers.splice(streamerIndex, 1)
			guildProfile.set("guildTwitch", dbData)
			guildProfile.markModified("guildTwitch")
			await guildProfile.save().catch(console.error)

			if (!await dbGuild.exists({ "guildTwitch.streamers.twitchUserId": user.id })) {
				const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id)
				await Promise.all(userSubs.data.map(async sub => { if (sub.transportMethod === "webhook" && (sub.type === "stream.online" || sub.type === "stream.offline")) await sub.unsubscribe() }))
				logConsole('twitch', 'listener_removed', { name: user.displayName, id: user.id })
			}

			return interaction.reply({ content: t(interaction.locale, "twitch.streamer_removed", { username }), flags: MessageFlags.Ephemeral })
		}
	}
}

export async function autocompleteRun(interaction: AutocompleteInteraction) {
	const query = interaction.options.getString("username", true)
	if (!query || query.length < 3) return interaction.respond([])

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

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

	const choices: ApplicationCommandOptionChoiceData[] = []
	const searchResult = await twitchClient.search.searchChannels(query)
	if (searchResult.data.length === 0) return interaction.respond([])

	searchResult.data.forEach(streamerResult => {
		if (dbData.streamers.some(s => s.twitchUserId === streamerResult.id)) return
		choices.push({ name: streamerResult.displayName, value: streamerResult.name })
	})

	return interaction.respond(choices)
}
