package stream.flarebot.flarebot.commands.secret.update;

import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.User;
import org.joda.time.Period;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
import stream.flarebot.flarebot.FlareBot;
import stream.flarebot.flarebot.Getters;
import stream.flarebot.flarebot.commands.CommandType;
import stream.flarebot.flarebot.commands.InternalCommand;
import stream.flarebot.flarebot.objects.GuildWrapper;
import stream.flarebot.flarebot.permissions.PerGuildPermissions;
import stream.flarebot.flarebot.scheduler.FlareBotTask;
import stream.flarebot.flarebot.scheduler.Scheduler;
import stream.flarebot.flarebot.util.MessageUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class UpdateCommand implements InternalCommand {

    private static AtomicBoolean queued = new AtomicBoolean(false);

    @Override
    public void onCommand(User sender, GuildWrapper guild, TextChannel channel, Message message, String[] args, Member member) {
        if (PerGuildPermissions.isCreator(sender)) {
            if (args.length == 0) {
                update(false, channel);
            } else if (args.length == 1) {
                if (args[0].equalsIgnoreCase("force")) {
                    update(true, channel);
                } else if (args[0].equalsIgnoreCase("no-active-channels")) {
                    channel.sendMessage("I will now update to the latest version when no channels are playing music!")
                            .queue();
                    if (Getters.getConnectedVoiceChannels() == 0) {
                        update(true, channel);
                    } else {
                        if (!queued.getAndSet(true)) {
                            FlareBot.NOVOICE_UPDATING.set(true);
                        } else
                            channel.sendMessage("There is already an update queued!").queue();
                    }
                } else if (args[0].equalsIgnoreCase("schedule")) {
                    if (!queued.getAndSet(true)) {
                        FlareBot.instance().scheduleUpdate();
                        MessageUtils.sendSuccessMessage("Update scheduled for 12PM GMT!", channel);
                    } else {
                        MessageUtils.sendErrorMessage("There is already an update queued!", channel);
                    }
                } else if (args[0].equalsIgnoreCase("cancel")) {
                    if (!queued.getAndSet(true)) {
                        MessageUtils.sendErrorMessage("There is no update queued!", channel);
                    } else {
                        if (Scheduler.cancelTask("Scheduled-Update")) {
                            MessageUtils.sendSuccessMessage("Cancelled Update!", channel);
                        } else {
                            MessageUtils.sendErrorMessage("Could not cancel update!", channel);
                        }
                    }
                } else {
                    if (!queued.getAndSet(true)) {
                        Period p;
                        try {
                            PeriodFormatter formatter = new PeriodFormatterBuilder()
                                    .appendDays().appendSuffix("d")
                                    .appendHours().appendSuffix("h")
                                    .appendMinutes().appendSuffix("m")
                                    .appendSeconds().appendSuffix("s")
                                    .toFormatter();
                            p = formatter.parsePeriod(args[0]);

                            new FlareBotTask("Scheduled-Update") {
                                @Override
                                public void run() {
                                    update(true, channel);
                                }
                            }.delay(TimeUnit.SECONDS.toMillis(p.toStandardSeconds().getSeconds()));
                        } catch (IllegalArgumentException e) {
                            channel.sendMessage("That is an invalid time option!").queue();
                            return;
                        }
                        channel.sendMessage("I will now update to the latest version in " + p.toStandardSeconds()
                                .getSeconds() + " seconds.")
                                .queue();
                    } else {
                        channel.sendMessage("There is already an update queued!").queue();
                    }
                }
            }
        }
    }

    /**
     * Update to the newest version of FlareBot!
     *
     * @param force   If the version number has not changed this will need to be true in order to update it.
     * @param channel Channel the command was sent in.
     */
    public static void update(boolean force, TextChannel channel) {
        if (force) {
            doTheUpdate(channel, "latest", FlareBot.getVersion());
            return;
        }
        try {
            URL url = new URL("https://raw.githubusercontent.com/FlareBot/FlareBot/master/pom.xml");
            BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
            String line;
            while (true) {
                line = br.readLine();
                if (line != null && (line.contains("<version>") && line.contains("</version>"))) {
                    String latestVersion = line.replace("<version>", "").replace("</version>", "").replaceAll(" ", "")
                            .replaceAll("\t", "");
                    String currentVersion = FlareBot.getVersion();
                    if (isHigher(latestVersion, currentVersion)) {
                        doTheUpdate(channel, latestVersion, currentVersion);
                    } else {
                        if (channel != null)
                            channel.sendMessage("I am currently up to date! Current version: `" + currentVersion + "`")
                                    .queue();
                    }
                    break;
                }
            }
        } catch (IOException e) {
            FlareBot.LOGGER.error("Could not update!", e);
        }
    }

    private static void doTheUpdate(TextChannel channel, String latestVersion, String currentVersion) {
        FlareBot.instance().setStatus("Updating..");
        if (channel != null)
            channel.sendMessage("Updating to version `" + latestVersion + "` from `" + currentVersion + "`").queue();
        FlareBot.UPDATING.set(true);
        FlareBot.instance().quit(true);
    }


    /**
     * Check if a string is higher than another.
     *
     * @param s1 This is the string that will be checked. Use this for things like latest version.
     * @param s2 This is the string being compared with. Use this for things like current version.
     * @return If s1 is greater than s2.
     */
    private static boolean isHigher(String s1, String s2) {
        String[] split = s1.split("\\.");
        int s1Major = Integer.parseInt(split[0]);
        int s1Minor = Integer.parseInt(split[1]);
        int s1Build = 0;
        if (split.length == 3)
            s1Build = Integer.parseInt(split[2]);

        String[] split2 = s2.split("\\.");
        int s2Major = Integer.parseInt(split2[0]);
        int s2Minor = Integer.parseInt(split2[1]);
        int s2Build = 0;
        if (split2.length == 3)
            s2Build = Integer.parseInt(split2[2]);

        return s1Major > s2Major || s1Minor > s2Minor || s1Build > s2Build;
    }

    @Override
    public String getCommand() {
        return "update";
    }

    @Override
    public String getDescription() {
        return "Update the bot.";
    }

    @Override
    public String getUsage() {
        return "{%}update [option]";
    }

    @Override
    public CommandType getType() {
        return CommandType.SECRET;
    }

    @Override
    public boolean isDefaultPermission() {
        return false;
    }
}