import { ApplicationCommandOptionType, Guild, GuildMember, Message, MessageCreateOptions, MessageReaction, Role, TextChannel, User } from 'discord.js'
import { v4 as uuid } from 'uuid'
import { client } from '../..'
import { config } from '../configuration'
import { Maybe } from '../interfaces'
import { logger } from '../logger'
import { Command } from '../structures/command'
import { RunOptions } from '../types/commandTypes'
import { isInitialAnnouncement } from '../helper/messageIdentifiers'

export default new Command({
	name: 'announce',
	description: 'Neues announcement im announcement Channel an alle senden.',
	options: [{
		name: "typ",
		type: ApplicationCommandOptionType.String,
		description: "Was für ein announcement?",
		choices: [{ name: "initial", value: "initial" }, { name: "votepls", value: "votepls" }, { name: "cancel", value: "cancel" }],
		required: true
	}],
	run: async (interaction: RunOptions) => {
		const command = interaction.interaction
		const requestId = uuid()
		if (!command.guildId) {
			logger.error("COMMAND DOES NOT HAVE A GUILD ID; CANCELLING!!!", { requestId })
			return
		}
		const guildId = command.guildId
		const announcementType = command.options.data.find(option => option.name.includes("typ"))
		logger.info(`Got command for announcing ${announcementType?.value}!`, { guildId, requestId })

		if (!announcementType) {
			logger.error("Did not get an announcement type!", { guildId, requestId })
			return
		}

		if (!isAdmin(command.member)) {
			logger.info(`Announcement was requested by ${command.member.displayName} but they are not an admin! Not sending announcement.`, { guildId, requestId })
			return
		} else {
			logger.info(`User ${command.member.displayName} seems to be admin`)
		}

		if ((<string>announcementType.value).includes("initial")) {
			sendInitialAnnouncement(guildId, requestId)
			command.followUp("Ist rausgeschickt!")
		} else {
			command.followUp(`${announcementType.value} ist aktuell noch nicht implementiert`)
		}
	}
})

function isAdmin(member: GuildMember): boolean {
	return member.roles.cache.find((role) => role.id === config.bot.jf_admin_role) !== undefined
}

async function sendInitialAnnouncement(guildId: string, requestId: string): Promise<void> {
	logger.info("Sending initial announcement")
	const announcementChannel: Maybe<TextChannel> = client.getAnnouncementChannelForGuild(guildId)
	if (!announcementChannel) {
		logger.error("Could not find announcement channel. Aborting", { guildId, requestId })
		return
	}

	const currentPinnedAnnouncementMessages = (await announcementChannel.messages.fetchPinned()).filter(message => isInitialAnnouncement(message))
	currentPinnedAnnouncementMessages.forEach(async (message) => await message.unpin())
	currentPinnedAnnouncementMessages.forEach(message => message.delete())

	const body = `[initial] Hey! @everyone! Hier ist der Watchparty Bot vom Hartzarett. 
    
Wir machen in Zukunft regelmäßig Watchparties in denen wir zusammen Filme gucken! Falls du mitmachen möchtest, reagiere einfach auf diesen Post mit 🎫, dann bekommst du automatisch eine Rolle zugewiesen und wirst benachrichtigt sobald es in der Zukunft weitere Watchparties und Filme zum abstimmen gibt.

Für eine Erklärung wie das alles funktioniert mach einfach /mitgucken für eine lange Erklärung am Stück oder /guides wenn du auswählen möchtest wozu du Infos bekommst.`

	const options: MessageCreateOptions = {
		allowedMentions: { parse: ['everyone'] },
		content: body
	}
	const message: Message<true> = await announcementChannel.send(options)
	await message.react("🎫")
	await message.pin()

}

export async function manageAnnouncementRoles(guild: Guild, reaction: MessageReaction, requestId: string) {
	const guildId = guild.id
	logger.info("Managing roles", { guildId, requestId })

	const announcementRole: Role | undefined = (await guild.roles.fetch()).find(role => role.id === config.bot.announcement_role)
	if (!announcementRole) {
		logger.error(`Could not find announcement role! Aborting! Was looking for role with id: ${config.bot.announcement_role}`, { guildId, requestId })
		return
	}

	const usersWhoWantRole: User[] = (await reaction.users.fetch()).filter(user => !user.bot).map(user => user)

	const allUsers = (await guild.members.fetch())

	const usersWhoHaveRole: GuildMember[] = allUsers
		.filter(member => member.roles.cache
			.find(role => role.id === config.bot.announcement_role) !== undefined)
		.map(member => member)

	const usersWhoNeedRoleRevoked: GuildMember[] = usersWhoHaveRole
		.filter(userWhoHas => !usersWhoWantRole.map(wanter => wanter.id).includes(userWhoHas.id))

	const usersWhoDontHaveRole: GuildMember[] = allUsers
		.filter(member => member.roles.cache
			.find(role => role.id === config.bot.announcement_role) === undefined)
		.map(member => member)

	const usersWhoNeedRole: GuildMember[] = usersWhoDontHaveRole
		.filter(userWhoNeeds => usersWhoWantRole.map(wanter => wanter.id).includes(userWhoNeeds.id))


	logger.debug(`Theses users will get the role removed: ${JSON.stringify(usersWhoNeedRoleRevoked)}`, { guildId, requestId })
	logger.debug(`Theses users will get the role added: ${JSON.stringify(usersWhoNeedRole)}`, { guildId, requestId })

	usersWhoNeedRoleRevoked.forEach(user => user.roles.remove(announcementRole))
	usersWhoNeedRole.forEach(user => user.roles.add(announcementRole))
}

