package com.github.games647.lagmonitor.command;

import com.github.games647.lagmonitor.LagMonitor;
import com.github.games647.lagmonitor.MethodMeasurement;
import com.github.games647.lagmonitor.Pages;
import com.github.games647.lagmonitor.task.MonitorTask;
import com.google.common.base.Strings;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.TimeUnit;

import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ClickEvent.Action;
import net.md_5.bungee.api.chat.ComponentBuilder;

import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

public class MonitorCommand extends LagCommand {

    public static final long SAMPLE_INTERVAL = 100L;
    public static final long SAMPLE_DELAY = TimeUnit.SECONDS.toMillis(1)  / 2;

    private MonitorTask monitorTask;

    public MonitorCommand(LagMonitor plugin) {
        super(plugin);
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!canExecute(sender, command)) {
            return true;
        }

        if (args.length > 0) {
            String monitorCommand = args[0].toLowerCase();
            switch (monitorCommand) {
                case "start":
                    startMonitor(sender);
                    break;
                case "stop":
                    stopMonitor(sender);
                    break;
                case "paste":
                    pasteMonitor(sender);
                    break;
                default:
                    sendError(sender, "Invalid command parameter");
            }
        } else if (monitorTask == null) {
            sendError(sender, "Monitor is not running");
        } else {
            List<BaseComponent[]> lines = new ArrayList<>();
            synchronized (this) {
                MethodMeasurement rootSample = monitorTask.getRootSample();
                printTrace(lines, 0, rootSample, 0);
            }

            Pages pagination = new Pages("Monitor", lines);
            pagination.send(sender);
            this.plugin.getPageManager().setPagination(sender.getName(), pagination);
        }

        return true;
    }

    private void printTrace(List<BaseComponent[]> lines, long parentTime, MethodMeasurement current, int depth) {
        String space = Strings.repeat(" ", depth);

        long currentTime = current.getTotalTime();
        float timePercent = current.getTimePercent(parentTime);

        String clazz = Pages.filterPackageNames(current.getClassName());
        String method = current.getMethod();
        lines.add(new ComponentBuilder(space + "[-] ")
                .append(clazz + '.')
                .color(ChatColor.DARK_AQUA)
                .append(method)
                .color(ChatColor.DARK_GREEN)
                .append(' ' + timePercent + "%")
                .color(ChatColor.GRAY)
                .create());

        Collection<MethodMeasurement> childInvokes = current.getChildInvokes().values();
        List<MethodMeasurement> sortedList = new ArrayList<>(childInvokes);
        Collections.sort(sortedList);

        sortedList.forEach((child) -> printTrace(lines, currentTime, child, depth + 1));
    }

    private void startMonitor(CommandSender sender) {
        Timer timer = plugin.getMonitorTimer();
        if (monitorTask == null && timer == null) {
            timer = new Timer(plugin.getName() + "-Monitor");
            plugin.setMonitorTimer(timer);

            monitorTask = new MonitorTask(plugin.getLogger(), Thread.currentThread().getId());
            timer.scheduleAtFixedRate(monitorTask, SAMPLE_DELAY, SAMPLE_INTERVAL);

            sender.sendMessage(ChatColor.DARK_GREEN + "Monitor started");
        } else {
            sendError(sender, "Monitor task is already running");
        }
    }

    private void stopMonitor(CommandSender sender) {
        Timer timer = plugin.getMonitorTimer();
        if (monitorTask == null && timer == null) {
            sendError(sender, "Monitor is not running");
        } else {
            monitorTask = null;
            if (timer != null) {
                timer.cancel();
                timer.purge();
                plugin.setMonitorTimer(null);
            }

            sender.sendMessage(ChatColor.DARK_GREEN + "Monitor stopped");
        }
    }

    private void pasteMonitor(final CommandSender sender) {
        Timer timer = plugin.getMonitorTimer();
        if (monitorTask == null && timer == null) {
            sendError(sender, "Monitor is not running");
        }

        Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
            String reportUrl = monitorTask.paste();
            if (reportUrl == null) {
                sendError(sender, "Error occurred. Please check the console");
            } else {
                String profileUrl = reportUrl + ".profile";
                send(sender, new ComponentBuilder("Report url: " + profileUrl)
                        .color(ChatColor.GREEN)
                        .event(new ClickEvent(Action.OPEN_URL, profileUrl))
                        .create());
            }
        });
    }
}