package de.yannicpunktdee.yoshibot.command;

import de.yannicpunktdee.yoshibot.main.YoshiBot;
import de.yannicpunktdee.yoshibot.utils.Logger;
import de.yannicpunktdee.yoshibot.utils.Resources;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message.Attachment;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.VoiceChannel;

import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * Abstrakte Superklasse für alle Kommandos.
 *
 * @author Yannic Link
 */
public abstract class YoshiCommand {
	
	protected final String[] requiredArguments = {};
	
	/**
	 * Der Kontext mit dem das Kommando aufgerufen wurde.
	 */
	protected YoshiCommandContext context;
	
	public static String resourceToDelete = null;
	
	
	/**
	 * Erzeugt ein neues Kommando, führt es aber noch nicht aus. Es wird ermittelt, ob die Argumentenkombination valide
	 * ist und das isOk-Flag gesetzt. Im Fehlerfall wird eine Fehleremeldung spezifiziert.
	 *
	 * @param context Der Kontext mit dem das Kommando aufgerufen wurde.
	 */
	public YoshiCommand(YoshiCommandContext context) {
		this.context = context;
	}
	
	/**
	 * Führt das Kommando aus.
	 *
	 * @return True, wenn Ausführung erfolgreich. False, wenn Ausführung fehlgeschlagen. Fehlermeldung wird in
	 * errorMessage spezifiziert.
	 */
	public boolean execute() {
		if (!context.containsArguments(requiredArguments)) {
			sendErrorMessage("Fehlende Argumente");
			return false;
		}
		return true;
	}
	
	protected final void sendMessage(String message) {
		EmbedBuilder eb = new EmbedBuilder();
		eb.setColor(Color.pink);
		eb.setDescription(message);
		context.getEvent().getTextChannel().sendMessage(eb.build()).queue();
	}
	
	protected final void sendFile(File file, String description) {
		EmbedBuilder eb = new EmbedBuilder();
		eb.setColor(Color.pink);
		if (description != null) eb.setDescription(description);
		context.getEvent().getTextChannel().sendFile(file).queue();
	}
	
	protected final void sendInfoMessage(String message) {
		EmbedBuilder eb = new EmbedBuilder();
		eb.setTitle("INFO");
		eb.setColor(Color.blue);
		eb.setDescription(message);
		context.getEvent().getTextChannel().sendMessage(eb.build()).queue();
	}
	
	protected final void sendErrorMessage(String message) {
		EmbedBuilder eb = new EmbedBuilder();
		eb.setTitle("ERROR");
		eb.setColor(Color.red);
		eb.setDescription(message);
		context.getEvent().getTextChannel().sendMessage(eb.build()).queue();
	}
	
	protected final void sendCustomMessage(MessageEmbed messageEmbed) {
		context.getEvent().getTextChannel().sendMessage(messageEmbed).queue();
	}
	
	
	protected File downloadAttachmentToFile(String directoryPath, String name) {
		if (directoryPath == null) directoryPath = Resources.getEnsuredTempPath();
		if (name == null) name = UUID.randomUUID().toString();
		
		if (!(new File(directoryPath)).isDirectory()) {
			Logger.logError("Das Download-Verzeichnis wurde nicht gefunden.");
			sendErrorMessage("Der Anhang konnte nicht gedownloaded werden.");
			return null;
		}
		
		List<Attachment> attachments = context.getEvent().getMessage().getAttachments();
		if (attachments.size() == 0) {
			return null;
		}
		Attachment attachment = attachments.get(0);
		
		File file = new File(directoryPath + name + "." + attachment.getFileExtension());
		CompletableFuture<File> future = attachment.downloadToFile(file);
		future.exceptionally(e -> {
			sendErrorMessage("Ein Anhang konnte nicht gedownloaded werden.");
			return null;
		});
		try {
			future.get();
		} catch (InterruptedException | ExecutionException e) {
			sendErrorMessage("Ein Anhang konnte nicht gedownloaded werden.");
			return null;
		}
		if (!file.exists()) {
			sendErrorMessage("Ein Anhang konnte nicht gedownloaded werden.");
			return null;
		}
		
		if (file.getAbsolutePath().endsWith(".mp3")) {
			String newFilePath = file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - 3) + "opus";
			Runtime rt = Runtime.getRuntime();
			try {
				String command = "/usr/bin/ffmpeg -y -i " + file.getAbsolutePath() + " " + newFilePath;
				Process pr = rt.exec(command);
				String err = new BufferedReader(new InputStreamReader(pr.getErrorStream())).lines()
						.collect(Collectors.joining());
				String out = new BufferedReader(new InputStreamReader(pr.getInputStream())).lines().collect(
						Collectors.joining());
				int exit = pr.waitFor();
				if (!file.delete()) {
					throw new IOException("Delete ging nich");
				}
				return new File(newFilePath);
			} catch (IOException | InterruptedException e) {
				e.printStackTrace();
			}
		}
		return file;
	}
	
	protected VoiceChannel getVoiceChannelByParam() {
		VoiceChannel vc;
		if (context.containsArguments(new String[]{"channel"})) {
			if (context.getArgument("channel") == null) return null;
			List<VoiceChannel> channels = YoshiBot.getInstance().jda
					.getVoiceChannelsByName(context.getArgument("channel"), true);
			if (!(channels.size() > 0)) {
				sendErrorMessage("Der Kanalname konnte nicht gefunden werden.");
				return null;
			}
			vc = channels.get(0);
		} else {
			try {
				vc = context.getEvent().getMember().getVoiceState().getChannel();
				if (vc == null) vc = YoshiBot.getInstance().getGuild().getAudioManager().getConnectedChannel();
			} catch (Exception e) {
				sendErrorMessage(
						"Es konnte kein Voicekanal gefunden werden in dem die Audio-Datei abgespielt werden kann.");
				return null;
			}
		}
		return vc;
	}
	
}
