package fr.Skydust.JdrBot.bases.CommandManager;

import fr.Skydust.JdrBot.bases.command.Command;
import fr.Skydust.JdrBot.bases.command.CommandSpec;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.reflections.Reflections;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class CommandsManager {
    private static List<Command> commandList;

    private static String startCmdChar = "";

    /**
     * Retrieves the main package name from the stacktrace.
     * @return The application's root package name.
     */
    public static String getMainClassPackageName()
    {
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        if (trace.length > 0) {
            String mainClassName = trace[trace.length - 1].getClassName();
            return mainClassName.split("\\.")[0];
        }
        return null;
    }

    /**
     * Finds every class annotated with CommandSpec and registers them as a command.
     * @param jda The instance to register commands for.
     * @param startChar The regex to use for the start character.
     */
    public static void registerCommands(JDA jda, String startChar) {
        if(commandList != null)
            throw new RuntimeException("Tried to call registerCommands twice !");

        if(startChar == null || startChar.isEmpty())
            throw new RuntimeException("The starting character of the bot cannot be empty !");

        String packageName = getMainClassPackageName();
        if(packageName == null || packageName.isEmpty())
            throw new RuntimeException("Could not determine the main package name.");

        commandList = new ArrayList<Command>();
        startCmdChar = startChar;

        Reflections reflections = new Reflections(packageName);
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(CommandSpec.class);

        for (Class<?> command: annotated) {
            try {
                Command cmd = (Command) command.getDeclaredConstructor().newInstance();
                commandList.add(cmd);

                System.out.println("Registered command "+ cmd.cmdName);
            } catch (InstantiationException | NoSuchMethodException | InvocationTargetException |
                     IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        jda.addEventListener(new CommandsManagerListener());
    }

    /**
     * Retrieve the command list.
     * @return A list of commands.
     */
    public static List<Command> getCommandList() {
        return commandList;
    }

    /**
     * Get the start character of commands.
     * @return A regex containing every cmd chars.
     */
    public static String getCmdChar() {
        return startCmdChar;
    }

    /**
     * Goes through the command list and runs it if it exists.
     * @param e The message received event from JDA.
     */
    public static void runCmd(MessageReceivedEvent e) {
        // IF it doesn't start with the startcmdchar, ignore for optimization
        if (!e.getMessage().getContentRaw().matches("^("+startCmdChar+").*$"))
            return;

        // Go through all cmds and call the one corresponding
        for (Command cmd : CommandsManager.getCommandList()) {
            if (isACommand(e.getMessage().getContentRaw(), cmd.cmdName)) {
                cmd.call(e);
                return;
            }
        }
    }

    /**
     * Returns if a message is a valid command.
     * @param message The original raw message to test.
     * @param cmd A specific command to check.
     * @return A boolean if the command is valid.
     */
    public static boolean isACommand(String message, String cmd) {
        return message.matches("^("+ startCmdChar +")("+ cmd +")( .*|)$");
    }
}
