import { SlashCommandBuilder } from 'discord.js';
import utils from '../../utils/videos.js';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
const { NODE_ENV } = process.env;
const ytdlpFormat = 'bestvideo[height<=?480]/best';

export default {
	data: new SlashCommandBuilder()
		.setName('vid2gif')
		.setDescription('Convert your video into a gif.')
		.addStringOption(option =>
			option.setName('url')
				.setDescription('URL of the video you want to convert')
				.setRequired(true))
		.addIntegerOption(option =>
			option.setName('quality')
				.setDescription('Quality of the gif conversion. Default 70. Number between 1 and 100')
				.setRequired(false))
		.addIntegerOption(option =>
			option.setName('fps')
				.setDescription('Change the speed at which the gif play at. Number between 1 and 100.')
				.setRequired(false))
		.addBooleanOption(option =>
			option.setName('autocrop')
				.setDescription('Autocrop borders on gif.')
				.setRequired(false))
		.addBooleanOption(option =>
			option.setName('noloop')
				.setDescription('Stop the gif from looping')
				.setRequired(false)),
	category: 'utility',
	alias: ['v2g', 'togif'],
	integration_types: [0, 1],

	async execute(interaction, args) {
		await interaction.deferReply({ ephemeral: false });
		const maxFileSize = await utils.getMaxFileSize(interaction.guild);
		const url = args.url;
		let quality = args.quality;
		if (quality) {
			if (quality <= 0) {
				quality = 1;
			}
			else if (quality > 100) {
				quality = 100;
			}
		}

		if (args.fps) {
			if (args.fps <= 0) {
				args.fps = 1;
			}
			else if (args.fps > 100) {
				args.fps = 100;
			}
		}

		if (!await utils.stringIsAValidurl(url)) {
			console.error(`Not a url!!! ${url}`);
			return interaction.editReply({ content: '❌ This does not look like a valid url!', ephemeral: true });
		}

		const aproxFileSize = await utils.getVideoSize(url, ytdlpFormat);
		console.log(aproxFileSize);

		if (aproxFileSize > 4) {
			return interaction.editReply('The file you are trying to convert is too big! Limit is 4 MB');
		};

		utils.downloadVideo(url, interaction.id, ytdlpFormat)
			.then(async () => {
				const file = fs.readdirSync(os.tmpdir()).filter(fn => fn.startsWith(interaction.id));
				let output = `${os.tmpdir()}/${file}`;

				if (args.autocrop) {
					const oldOutput = output;
					output = `${os.tmpdir()}/autocrop${file}`;
					await utils.autoCrop(oldOutput, output);
				}

				// Get the fps of the original video
				if (!args.fps) {
					args.fps = getVideoFramerate(output);
				}

				const gifskiOutput = output.replace(path.extname(output), '.gif');
				const gifsicleOutput = output.replace(path.extname(output), 'gifsicle.gif');

				// Extract every frame for gifski
				await utils.ffmpeg(['-i', output, `${os.tmpdir()}/frame${interaction.id}%04d.png`]);
				// Make it look better
				await gifski(gifskiOutput, `${os.tmpdir()}/frame${interaction.id}*`, quality, await args.fps);
				// Optimize it
				await gifsicle(gifskiOutput, gifsicleOutput, args.noloop);

				const fileStat = fs.statSync(gifsicleOutput);
				const fileSize = fileStat.size / 1000000.0;

				if (fileSize > 25) {
					await interaction.deleteReply();
					await interaction.followUp('❌ Uh oh! The video once converted is too big!', { ephemeral: true });
				}
				else if (fileSize > maxFileSize) {
					const fileURL = await utils.upload(gifsicleOutput)
						.catch(err => {
							console.error(err);
						});
					await interaction.editReply({ content: `â„šī¸ File was bigger than ${maxFileSize} mb. It has been uploaded to an external site.\n${fileURL}`, ephemeral: false });
				}
				else {
					await interaction.editReply({ files: [gifsicleOutput], ephemeral: false });
				}
			});
	},
};

async function gifski(output, input, quality, fps) {
	return await new Promise((resolve, reject) => {
		// Shell: true should be fine as no user input is being passed
		execFile('gifski', ['--quality', quality ? quality : 70, '--fps', fps ? fps : 20, '-o', output, input], { shell: true }, (err, stdout, stderr) => {
			if (err) {
				reject(stderr);
			}
			if (stderr) {
				console.error(stderr);
			}
			console.log(NODE_ENV === 'development' ? stdout : null);
			resolve();
		});
	});
}

async function gifsicle(input, output, loop = false) {
	return await new Promise((resolve, reject) => {
		// Shell: true should be fine as no user input is being passed
		execFile('gifsicle', ['--colors', '256', loop ? '--no-loopcount' : '', '-i', input, '-o', output], { shell: true }, (err, stdout, stderr) => {
			if (err) {
				reject(stderr);
			}
			if (stderr) {
				console.error(stderr);
			}
			console.log(NODE_ENV === 'development' ? stdout : null);
			resolve();
		});
	});
}

async function getVideoFramerate(input) {
	return await new Promise((resolve, reject) => {
		execFile('ffprobe', ['-v', 'error', '-of', 'csv=p=0', '-select_streams', 'v:0', '-show_entries', 'stream=r_frame_rate', input], (err, stdout, stderr) => {
			if (err) {
				reject(stderr);
			}
			if (stderr) {
				console.error(stderr);
			}
			const tempfps = stdout.trim().split('/');
			const fps = tempfps[0] / tempfps[1];
			return resolve(fps);
		});
	});
}