// Include Discordjs library
// http://github.com/discordjs/
const { Client, VoiceChannel, Intents } = require('discord.js');
// Include fs, for reading and writing files
const fs = require('fs');
// Include ytdl, so we can play youtube videos over voice
const ytdl = require('ytdl-core-discord');
// Include our local auth key, so that we don't leak keys
const auth = require('./auth.json');
// Volume for playback (fraction)
const volume = 0.60;

// **** PROGRAM START ****
// This program is 2 lines of code, the rest is callback function definition, wherein the bot magic happens

// Initialize a new Discord Client, and tell Discord what updates we are interested in recieving
// https://discord.com/developers/docs/topics/gateway#list-of-intents
const client = new Client({ intents: [Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS] });

// Define Sigtrap Callback for clean shutdowns
function signalExit(signal) {
	console.log("\tCaught ${signal}");
	// Kills voice connections, and logs the client out
	client.destroy();
	// Exit with return code 0
	process.exit(0);
}
// Subscribe to a few signal events
process.on('SIGINT', signalExit); // For Ctrl-C when running in a TTY
process.on('SIGTERM', signalExit); // for systemd's stoppping of services

// Define Several Discord Related Callbacks

// This one only triggers when the connection to Discord is sucessful.  Usually this is once at the start,
// But if the connection to Discord hiccups for any reason, this will trigger again.
client.on('ready', () => { console.log(`Logged in as ${client.user.tag}!`); });

// Here's the brains of the operation.  This triggers every time a message is sent in any server the bot is in.
// the async modifier really means async and concurently.  Consider thread safety when interacting with globals.
// The Message Object is our keyhole into Discord, where we have lots of fields and objects to work with.
// https://discord.js.org/#/docs/main/v12/class/Message
client.on('message', async message => {
	// Filter Section
	// In the event of a DM...
	if (message.guild == null) {
		switch (message.author.id) {
			case "262406433201586177": // Weegee
			case "137358406821347328": // moosecrap
			case "144317130026778624": // Piroglith
			case "137293315883139072": // tks_ftw
			case "172538370440953867": // Brightland
			case "183009699300507648": // spamminn
			case "209154502412992514": // NariNaju
			case "198278682677084160": // DrunkZombie
			case "137349560610586624": // craze
			case "305491553198145539": // Sam
			case "174356437550497792": // NoobWantsHacks
			case "308412988329558017": // Revanite
				// TODO: Recieve sound files from trusted parties?
				return;
			default:
				console.log(`Ignoring DM from ${message.author.tag}: ${message.content}`);
				return;
		}
	}
	// Filter Authorized Guilds
	switch (message.guild.id) {
		case "860344888301846538": // ZeSkypeBot Incubator
		case "305204527152365581": // Goldar Squad
			break;
		default:
			console.log(`Message from Unauthorized Guild ${message.guild.name}!`);
			return;
	}
	// Pre-Work
	// Do a few things that may be helpful
	const d = new Date();
	// Process Section
	// Check for the shutup message
	if (message.content === "~") {
		console.log(`!stop issued by ${message.author.tag}`);
		if (!message.member.voice.channel) {
			message.channel.send('you can\'t stop the party if you\'re not here');
		} else {
			message.member.voice.channel.leave();
		}
		return;
	}
	// Try to find a sound file that matches the message
	if (!message.content.match(/[ \/;]/)) {
		// we had to add '/' because moose tried to 'play' ../../../../etc/shadow
		// Construct the expected sound path
		const soundPath = `./sounds/${message.content}.ogg`;
		if (message.author.id === "172538370440953867") {
			switch(message.content) {
				case "butthash":
					message.react('859687015247511561');
				default:
			}
		}
		if (message.author.id === "144317130026778624") {
			switch(message.content) {
				case "daytona":
				case "e":
					if ([0,5,6].includes(d.getDay()) && Math.random() < 0.8) return;
				default:
			}
		}
		// TODO: this try block optional?
		try {
			// Test if there is a file at the expected path
			if (message.member.voice.channel && fs.existsSync(soundPath)) {
				console.log(`Sound '${message.content}' issued by ${message.author.tag} ${message.author.id}`);
				// Join the Voice Channel of the author
				const connection = await message.member.voice.channel.join();
				// TODO: Switch to fs-based file read?
				// Play the sound file as requested
				const dispatcher = connection.play(soundPath, { volume: volume });
				dispatcher.on('start', () => { console.log(`Playing ${soundPath}`); });
				dispatcher.on('finish', () => { console.log(`Finished ${soundPath}`); });
				dispatcher.on('error', console.error);
				return;
			}
			// else no sound file, fall through past catch block
		} catch(err) {
			console.error(err);
			return;
		}
	}
	// Otherwise process commands
	if (message.content === "!help" ) {
		console.log(`!help issued by ${message.author.tag} ${message.author.id}`);
		var response = 'ZeSkypeBot v6.0.6 \'black pepper\'\n';
		response += 'Original by moosecrap#5953 for Skype, Remade by Weegee#6402 for Discord\n';
		response += 'Changelog:\n';
		response += 'v6.0.6 Changes to !play regex in pursuit of playing at timestamps\n';
		response += 'v6.0.5	Added feedback for cases where ytdl crashes\n';
		response += 'v6.0.3	Changed !play regex per user feedback and counterexamples\n';
		response += 'v6.0.2	Removed response to voiceless sound commands, for being too spammy\n';
		response += 'v6.0.1	Added !play for youtube videos\n';
		response += 'v6.0.0	Public release, plays sounds from Goldar Squad\'s collection of micspam audio files.  No list will be provided\n';
		//response += 'v6.0.0	Changed version numbers to match moosecrap\'s, removed sound file extension requirement\n';
		//response += 'v0.2.0	Added !help command and printout\n';
		//response += 'v0.1.0	Initial Release - Can play sounds from file\n';
		response += 'Commands:\n';
		response += '~		stop any sounds in progress and disconnect from voice\n';
		response += '!help	print this message\n';
		response += 'To play Sounds, first join a voice channel then send a message in the text chat with the name of the sound\n';
		response += '!play [youtube url]	play a youtube video over chat\n';
		message.channel.send(response);
		return;
	}
	// Check for youtube play command
	const ytmatch = message.content.match(/(?<=!play (https?:\/\/)(www\.)?youtu((be\.com\/watch\?v=)|(\.be\/)))[\w-]{11}/);
	if (ytmatch && message.member.voice.channel) {
		const timematch = message.content.match(/(?<=t=)(\d{1,2}h)?(\d{1,2}m)?\d{1,4}s?(?=$)/);
		var timestamp = '0';
		if (timematch) {
			timestamp = timematch[0];
		}
		console.log(`!play issued for ${ytmatch[0]} by ${message.author.tag} ${message.author.id}`);
		// Reconstruct a youtu.be url for the supplied video
		const yturl = `https://youtu.be/${ytmatch[0]}`;
		//TODO: Factor out common code with the soundbyte play?
		const connection = await message.member.voice.channel.join();
		// Play the video as requested
		try {
			//TODO: process the timestamp with ffmpeg or whatever
			const dispatcher = connection.play(await ytdl(yturl, { begin: timestamp }), { type: 'opus', volume: volume});
			dispatcher.on('start', () => { console.log(`Playing ${yturl}`); });
			dispatcher.on('finish', () => { console.log(`Finished ${yturl}`); });
			dispatcher.on('error', console.error);
		} catch (error) {
			console.error(error);
			message.channel.send("Bro youtube says this shit is porn, i aint playing that");
			const dispatcher = connection.play('./sounds/cantlet.ogg');
		}
		return;
	}

	// Generic message responses
	if (message.content.match(/https:\/\/ootbingo\.github\.io/)) {
		var response = 'ooh its bingo time, i call ';
		if (Math.floor(Math.random()*2)) { response += 'over'; }
		else { response += 'under'; }
		response += ' on gateskip';
		message.channel.send(response);
		return;
	}
});
// Now our client is prepared, we can log in.  Callbacks defined previously 
client.login(auth.token);
