'use strict';

const config = require('config');
const timers = require('timers/promises');

const questions = require('../../assets/jeopardy.json');

const settings = {
	rounds: config.trivia.rounds,
	timeout: config.trivia.timeout,
	mode: config.trivia.mode,
};

let game = null;

function shuffle(unshuffledQuestions, limit = 10) {
	const shuffled = unshuffledQuestions;

	for (let i = shuffled.length - 1; i > 0; i -= 1) {
		const j = Math.floor(Math.random() * (i + 1));
		const temp = shuffled[i];

		shuffled[i] = shuffled[j];
		shuffled[j] = temp;
	}

	return shuffled.slice(0, limit);
}

function scoreRound(context, round) {
	if (game.answers.length === 0) {
		return `No one scored in round ${round + 1}, better luck next time!`;
	}

	return game.answers.map(({ user }) => {
		context.setPoints(user, 1);
		game.points[user.username] = (game.points[user.username] || 0) + 1;

		return `**@${user.username}** gets a point`;
	}).join(', ');
}

async function playRound(context, round = 0) {
	const ac = new AbortController(); // eslint-disable-line no-undef
	const now = new Date();

	game.answers = [];
	game.round = round;
	game.ac = ac;

	const question = game.questions[round];

	context.sendMessage(`**Question ${round + 1}/${game.questions.length}**: ${question.question}`, context.room.id);
	context.logger.info(`Trivia asked "${question.question}" with answer: ${question.answer}`);

	try {
		await timers.setTimeout(game.timeout * 1000, null, {
			signal: ac.signal,
		});
	} catch (error) {
		// abort expected, not an error
	}

	if (!ac.signal.aborted) {
		ac.abort();
	}

	if (game.stopped) {
		context.sendMessage(`The game was stopped by ${game.stopped.username}. The answer to the last question was: **${question.answer}**`, context.room.id);
		game = null;

		return;
	}

	if (game.answers.length === 0) {
		context.sendMessage(`**TIME'S UP!** No one guessed the answer: **${question.answer}**`, context.room.id);
	} else {
		const scores = scoreRound(context, round);

		if (game.mode === 'first') {
			context.sendMessage(`**${question.answer}** is the right answer, played in **${((new Date() - now) / 1000).toFixed(3)}s**! ${scores}`, context.room.id);
		}

		if (game.mode === 'timeout') {
			context.sendMessage(`**STOP!** The correct answer is **${question.answer}**. ${scores}`, context.room.id);
		}
	}

	if (round < game.questions.length - 1) {
		await timers.setTimeout(5000);

		if (game.stopped) {
			context.sendMessage(`The game was stopped by ${game.stopped.username}`, context.room.id);
			game = null;

			return;
		}

		playRound(context, round + 1);
		return;
	}

	const leaders = Object.entries(game.points).sort(([, scoreA], [, scoreB]) => scoreB - scoreA).map(([username, score], index) => {
		if (index === 0) {
			return `**@${username}** with **${score}** points`;
		}

		return `**@${username}** with **${score}** points`;
	}).join(', ');
	context.sendMessage(`That's the end of the game! Best players: ${leaders}`, context.room.id);

	game = null;
}

async function start(context) {
	const roundQuestions = shuffle(questions, settings.rounds);

	game = {
		round: 0,
		questions: roundQuestions,
		answers: [],
		points: {},
		...settings,
	};

	playRound(context, 0);
}

async function stop(context) {
	game.stopped = context.user;
	game.ac.abort();
}

function onCommand(args, context) {
	if (!context.subcommand && !game) {
		start(context);
		return;
	}

	if (!context.subcommand && game) {
		context.sendMessage(`There is already a game going on! The current question for round ${game.round + 1} is: ${game.questions[game.round].question}`, context.room.id);
		return;
	}

	if (context.subcommand === 'stop' && game) {
		stop(context);
	}

	if (context.subcommand === 'stop' && !game) {
		context.sendMessage(`There is no game going on at the moment. Start one with ${config.prefix}trivia!`, context.room.id);
	}
}

async function onMessage(message, context) {
	if (!game) {
		return;
	}

	const { answer } = game.questions[game.round];

	if (new RegExp(answer, 'i').test(message.body)) {
		game.answers.push({
			user: context.user,
			answer: message.body,
		});

		if (settings.mode === 'first') {
			game.ac.abort();
		}
	}
}

module.exports = {
	name: 'Trivia',
	onCommand,
	onMessage,
};
