import { SlashCommandBuilder, MessageFlags } from "discord.js"
import type { ChatInputCommandInteraction, AutocompleteInteraction, GuildMember } from "discord.js"
import { useMainPlayer, useQueue } from "discord-player"
import { SpotifyExtractor } from "@discord-player/extractor"
import { YoutubeiExtractor } from "discord-player-youtubei"
import { startProgressSaving } from "@/utils/player"
import type { TrackSearchResult } from "@/types/player"
import type { GuildPlayer } from "@/types/schemas"
import dbGuild from "@/schemas/guild"
import { t } from "@/utils/i18n"
import { logConsoleError } from "@/utils/console"

export const data = new SlashCommandBuilder()
	.setName("play")
	.setDescription("Play a song")
	.setNameLocalizations({ fr: "jouer" })
	.setDescriptionLocalizations({ fr: "Jouer une musique" })
	.addStringOption(option => option
		.setName("search")
		.setDescription("Music title to search for")
		.setNameLocalizations({ fr: "recherche" })
		.setDescriptionLocalizations({ fr: "Titre de la musique à chercher" })
		.setRequired(true)
		.setAutocomplete(true)
	)

export async function execute(interaction: ChatInputCommandInteraction) {
	const member = interaction.member as GuildMember
	const voiceChannel = member.voice.channel
	if (!voiceChannel) return interaction.reply({ content: t(interaction.locale, "player.not_in_voice"), flags: MessageFlags.Ephemeral })

	const botChannel = interaction.guild?.members.me?.voice.channel
	if (botChannel && voiceChannel.id !== botChannel.id) return interaction.reply({ content: t(interaction.locale, "player.not_in_same_voice"), flags: MessageFlags.Ephemeral })

	await interaction.deferReply()

	const query = interaction.options.getString("search", true)
	const player = useMainPlayer()
	let queue = useQueue(interaction.guild?.id ?? "")

	if (!queue) {
		if (interaction.guild) queue = player.nodes.create(interaction.guild, {
			metadata: {
				channel: interaction.channel,
				client: interaction.guild.members.me,
				requestedBy: interaction.user
			},
			selfDeaf: true,
			volume: 20,
			leaveOnEmpty: true,
			leaveOnEmptyCooldown: 30000,
			leaveOnEnd: true,
			leaveOnEndCooldown: 300000
		})
		else return
	}

	try { if (!queue.connection) await queue.connect(voiceChannel) }
	catch (error) { logConsoleError('discord_player', 'play.connect_error', {}, error as Error) }

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

	const botId = interaction.client.user.id
	const dbData = guildProfile.get("guildPlayer") as GuildPlayer
	dbData.instances ??= []

	const instanceIndex = dbData.instances.findIndex(instance => instance.botId === botId)
	const instance = { botId, replay: {
		textChannelId: interaction.channel?.id ?? "",
		voiceChannelId: voiceChannel.id,
		trackUrl: "",
		progress: 0
	} }

	if (instanceIndex === -1) dbData.instances.push(instance)
	else dbData.instances[instanceIndex] = instance

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

	const result = await player.search(query, { requestedBy: interaction.user })
	if (!result.hasTracks()) return interaction.followUp({ content: t(interaction.locale, "player.no_track_found", { query }), flags: MessageFlags.Ephemeral })
	const track = result.tracks[0]
	if (process.env.NODE_ENV === "development") console.log(query, result, track)

	const entry = queue.tasksQueue.acquire()
	await entry.getTask()
	queue.addTrack(track)

	try {
		if (!queue.isPlaying()) await queue.node.play()
		startProgressSaving(queue.guild.id, botId)
		const track_source = track.source === "spotify" ? t(interaction.locale, "player.sources.spotify") : track.source === "youtube" ? t(interaction.locale, "player.sources.youtube") : t(interaction.locale, "player.sources.unknown")
		return await interaction.followUp(t(interaction.locale, "player.loading_track", { title: track.title, author: track.author, source: track_source }))
	}
	catch (error) { logConsoleError('discord_player', 'play.execution_error', {}, error as Error) }
	finally { queue.tasksQueue.release() }
}

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

	const player = useMainPlayer()

	const resultsSpotify = await player.search(query, { searchEngine: `ext:${SpotifyExtractor.identifier}` })
	const resultsYouTube = await player.search(query, { searchEngine: `ext:${YoutubeiExtractor.identifier}` })
	if (process.env.NODE_ENV === "development") console.log(resultsSpotify, resultsYouTube)

	const tracksSpotify = resultsSpotify.tracks.slice(0, 5).map(t => ({
		name: `Spotify: ${`${t.title} - ${t.author} (${t.duration})`.length > 75 ? `${`${t.title} - ${t.author}`.substring(0, 75)}... (${t.duration})` : `${t.title} - ${t.author} (${t.duration})`}`,
		value: t.url
	}))
	const tracksYouTube = resultsYouTube.tracks.slice(0, 5).map(t => ({
		name: `YouTube: ${`${t.title} - ${t.author} (${t.duration})`.length > 75 ? `${`${t.title} - ${t.author}`.substring(0, 75)}... (${t.duration})` : `${t.title} - ${t.author} (${t.duration})`}`,
		value: t.url
	}))

	const tracks: TrackSearchResult[] = []
	tracksSpotify.forEach((t) => tracks.push({ name: t.name, value: t.value }))
	tracksYouTube.forEach((t) => tracks.push({ name: t.name, value: t.value }))

	return interaction.respond(tracks)
}
