const { Command } = require('discord.js-commando');
const { Collection } = require('discord.js');
const { stripIndents } = require('common-tags');
const { shuffle, wait, awaitPlayers } = require('../../util/Util');
const { questions, stories } = require('../../assets/json/wizard-convention');

module.exports = class WizardConventionCommand extends Command {
	constructor(client) {
		super(client, {
			name: 'wizard-convention',
			aliases: ['wiz-convention'],
			group: 'games',
			memberName: 'wizard-convention',
			description: 'Who is the Dragon? Who is the healer? Who is the mind reader? Will the Dragon eat them all?',
			guildOnly: true
		});

		this.playing = new Set();
	}

	async run(msg) { // eslint-disable-line complexity
		if (this.playing.has(msg.channel.id)) return msg.reply('Only one game may be occurring per channel.');
		this.playing.add(msg.channel.id);
		try {
			await msg.say('You will need at least 2 more players, at maximum 10. To join, type `join game`.');
			const awaitedPlayers = await awaitPlayers(msg, 10, 3);
			if (!awaitedPlayers) {
				this.playing.delete(msg.channel.id);
				return msg.say('Game could not be started...');
			}
			const players = await this.generatePlayers(awaitedPlayers);
			let turn = 1;
			while (players.size > 2 && players.exists('role', 'dragon')) {
				let eaten = null;
				let healed = null;
				await msg.say(`Night ${turn}, sending DMs...`);
				for (const player of players.values()) {
					if (player.role.includes('pleb')) continue;
					await msg.say(`The ${player.role} is making their decision...`);
					const valid = players.filterArray(p => p.role !== player.role);
					await player.user.send(stripIndents`
						${questions[player.role]} Please type the number.
						${valid.map((p, i) => `**${i + 1}.** ${p.user.tag}`).join('\n')}
					`);
					const filter = res => valid[Number.parseInt(res.content, 10) - 1];
					const decision = await player.user.dmChannel.awaitMessages(filter, {
						max: 1,
						time: 120000
					});
					if (!decision.size) {
						await player.user.send('Sorry, time is up!');
						continue;
					}
					const choice = Number.parseInt(decision.first().content, 10);
					if (player.role === 'dragon') {
						const chosen = players.get(choice);
						eaten = chosen.id;
						await player.user.send(`${chosen.user.tag} will be eaten...`);
					} else if (player.role === 'healer') {
						const chosen = players.get(choice);
						healed = chosen.id;
						await player.user.send(`${chosen.user.tag} will be healed...`);
					} else if (player.role === 'mind reader') {
						await player.user.send(players.find('role', 'dragon').id === choice ? 'Yes.' : 'No.');
					}
				}
				const display = eaten ? players.get(eaten).user : null;
				const story = stories[Math.floor(Math.random() * stories.length)];
				if (eaten && eaten !== healed) players.delete(eaten);
				if (eaten && eaten === healed) {
					await msg.say(stripIndents`
						Late last night, a dragon emerged and tried to eat ${display}${story}
						Thankfully, a healer stepped in just in time to save the day.
						Who is this mysterious dragon? You have one minute to decide.
					`);
				} else if (eaten && players.size < 3) {
					await msg.say(stripIndents`
						Late last night, a dragon emerged and devoured poor ${display}${story}
						Sadly, after the event, the final wizard ran in fear, leaving the dragon to rule forever.
					`);
					break;
				} else if (eaten && eaten !== healed) {
					await msg.say(stripIndents`
						Late last night, a dragon emerged and devoured poor ${display}${story}
						Who is this mysterious dragon? You have one minute to decide.
					`);
				} else {
					await msg.say(stripIndents`
						Late last night, a dragon emerged. Thankfully, however, it didn't try to eat anyone.
						Who is this mysterious dragon? You have one minute to decide.
					`);
				}
				await wait(60000);
				const playersArr = Array.from(players.values());
				await msg.say(stripIndents`
					Who do you think is the dragon? Please type the number.
					${playersArr.map((p, i) => `**${i + 1}.** ${p.user.tag}`).join('\n')}
				`);
				const voted = [];
				const filter = res => {
					if (!players.exists(p => p.user.id === res.author.id)) return false;
					if (voted.includes(res.author.id)) return false;
					if (!playersArr[Number.parseInt(res.content, 10) - 1]) return false;
					voted.push(res.author.id);
					return true;
				};
				const votes = await msg.channel.awaitMessages(filter, {
					max: players.size,
					time: 120000
				});
				if (!votes.size) {
					await msg.say('No one will be expelled.');
					continue;
				}
				const expelled = this.getExpelled(votes, players, playersArr);
				await msg.say(`${expelled.user} will be expelled.`);
				players.delete(expelled.id);
				++turn;
			}
			this.playing.delete(msg.channel.id);
			const dragon = players.find('role', 'dragon');
			if (!dragon) return msg.say('The dragon has been vanquished! Thanks for playing!');
			return msg.say(`Oh no, the dragon wasn't caught in time... Nice job, ${dragon.user}!`);
		} catch (err) {
			this.playing.delete(msg.channel.id);
			return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
		}
	}

	async generatePlayers(list) {
		let roles = ['dragon', 'healer', 'mind reader'];
		for (let i = 0; i < (list.length - 2); i++) roles.push(`pleb ${i + 1}`);
		roles = shuffle(roles);
		const players = new Collection();
		let i = 0;
		for (const user of list) {
			players.set(user.id, {
				id: user.id,
				user,
				role: roles[i]
			});
			await user.send(`Your role will be: ${roles[i]}!`);
			i++;
		}
	}

	getExpelled(votes, players, playersArr) {
		const counts = new Collection();
		for (const vote of votes.values()) {
			const player = players.get(playersArr[Number.parseInt(vote.content, 10) - 1].id);
			if (counts.has(player.id)) {
				++counts.get(player.id).votes;
			} else {
				counts.set(player.id, {
					id: player.id,
					votes: 1,
					user: player.user
				});
			}
		}
		return counts.sort((a, b) => b.votes - a.votes).first();
	}
};
