package nl.sbdeveloper.themeparkplus.commands;

import com.cryptomorin.xseries.XMaterial;
import nl.iobyte.themepark.ThemePark;
import nl.sbdeveloper.themeparkplus.ThemeParkPlus;
import nl.sbdeveloper.themeparkplus.api.PlusAPI;
import nl.sbdeveloper.themeparkplus.api.enums.WalkingDirection;
import nl.sbdeveloper.themeparkplus.api.objects.Gate;
import nl.sbdeveloper.themeparkplus.util.ConfigUtil;
import nl.sbdeveloper.themeparkplus.util.Cuboid;
import nl.sbdeveloper.themeparkplus.util.LGUtil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.StringUtil;

import java.util.*;
import java.util.stream.Collectors;

public class TPPCMD implements TabExecutor {

    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if (args.length == 0) {
            return helpCommand(sender);
        } else if (args[0].equalsIgnoreCase("info") && args.length == 1) {
            return infoCommand(sender);
        } else if (args[0].equalsIgnoreCase("opengate") && args.length == 5) {
            if (!sender.hasPermission("tpp.opengate")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return openGate(sender, args, 0, null);
        } else if (args[0].equalsIgnoreCase("opengate") && args.length == 6) {
            if (!sender.hasPermission("tpp.opengate")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }

            try {
                int amount = Integer.parseInt(args[5]);
                return openGate(sender, args, amount, null);
            } catch (NumberFormatException ex) {
                try {
                    WalkingDirection dir = WalkingDirection.valueOf(args[5]);

                    return openGate(sender, args, 0, dir);
                } catch (Exception ex2) {
                    sender.sendMessage(ConfigUtil.getMessage("Gates.UnknownDirAndAmount"));
                    return true;
                }
            }
        } else if (args[0].equalsIgnoreCase("opengate") && args.length == 7) {
            if (!sender.hasPermission("tpp.opengate")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }

            int amount;
            WalkingDirection dir;

            try {
                amount = Integer.parseInt(args[5]);
            } catch (NumberFormatException ex) {
                sender.sendMessage(ConfigUtil.getMessage("General.IncorrectAmount"));
                ex.printStackTrace();
                return true;
            }

            try {
                dir = WalkingDirection.valueOf(args[6]);
            } catch (Exception ex2) {
                sender.sendMessage(ConfigUtil.getMessage("Gates.UnknownDir"));
                return true;
            }

            return openGate(sender, args, amount, dir);
        } else if (args[0].equalsIgnoreCase("closegate") && args.length == 5) {
            if (!sender.hasPermission("tpp.closegate")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }

            return closeGate(sender, args);
        } else if (args[0].equalsIgnoreCase("lampon") && args.length == 5) {
            if (!sender.hasPermission("tpp.lampon")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return lampTurnOnCommand(sender, args, 0);
        } else if (args[0].equalsIgnoreCase("lampon") && args.length == 6) {
            if (!sender.hasPermission("tpp.lampon")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }

            int amount;
            try {
                amount = Integer.parseInt(args[5]);
            } catch (NumberFormatException ex) {
                sender.sendMessage(ConfigUtil.getMessage("General.IncorrectAmount"));
                return true;
            }

            return lampTurnOnCommand(sender, args, amount);
        } else if (args[0].equalsIgnoreCase("lampoff") && args.length == 5) {
            if (!sender.hasPermission("tpp.lampoff")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return lampTurnOffCommand(sender, args);
        } else if (args[0].equalsIgnoreCase("lampson") && args.length == 8) {
            if (!sender.hasPermission("tpp.lampson")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return lampsTurnOnCommand(sender, args, 0);
        } else if (args[0].equalsIgnoreCase("lampson") && args.length == 9) {
            if (!sender.hasPermission("tpp.lampson")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }

            int amount;
            try {
                amount = Integer.parseInt(args[8]);
            } catch (NumberFormatException ex) {
                sender.sendMessage(ConfigUtil.getMessage("General.IncorrectAmount"));
                return true;
            }

            return lampsTurnOnCommand(sender, args, amount);
        } else if (args[0].equalsIgnoreCase("lampsoff") && args.length == 8) {
            if (!sender.hasPermission("tpp.lampsoff")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return lampsTurnOffCommand(sender, args);
        } else if (args[0].equalsIgnoreCase("givefpticket") && (args.length == 2 || args.length == 3)) {
            if (!sender.hasPermission("tpp.givefpticket")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return giveFPTicketCommand(sender, args);
        } else if (args[0].equalsIgnoreCase("redstonetimer") && (args.length == 6 || args.length == 7)) {
            if (!sender.hasPermission("tpp.redstonetimer")) {
                sender.sendMessage(ConfigUtil.getMessage("General.NoPermission"));
                return true;
            }
            return redstoneTimerCommand(sender, args);
        }
        return helpCommand(sender);
    }

    private boolean redstoneTimerCommand(CommandSender sender, String[] args) {
        World bworld = Bukkit.getWorld(args[1]);
        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);
        Location loc = new Location(bworld, bx, by, bz);

        long onDelayTicks = Long.parseLong(args[5]) * 20L;
        long offDelayTicks = args.length == 7 ? Long.parseLong(args[6]) * 20L : 20L;

        final Block block = loc.getBlock();
        final Material oldMaterial = block.getType();

        Bukkit.getScheduler().scheduleSyncDelayedTask(ThemeParkPlus.getInstance(), () -> {
            block.setType(XMaterial.REDSTONE_BLOCK.parseMaterial());
            Bukkit.getScheduler().scheduleSyncDelayedTask(ThemeParkPlus.getInstance(), () -> {
                BlockState state = block.getState();
                state.setType(oldMaterial);
                state.update(true);
            }, offDelayTicks);
        }, onDelayTicks);

        HashMap<String, String> replacements = new HashMap<>();
        replacements.put("%sec1%", args[5]);
        replacements.put("%sec2%", args.length == 7 ? args[6] : "" + 1);

        sender.sendMessage(ConfigUtil.getMessage("RedstoneTimer.Started", replacements));
        return true;
    }

    private boolean giveFPTicketCommand(CommandSender sender, String[] args) {
        if (!ThemeParkPlus.hasParkHook()) {
            sender.sendMessage(ConfigUtil.getMessage("General.RequiresThemePark"));
            return true;
        }

        if (args.length == 2 && !(sender instanceof Player)) {
            sender.sendMessage(ConfigUtil.getMessage("General.NoPlayer"));
            return true;
        }

        if (!nl.iobyte.themepark.ThemePark.getInstance().getAPI().getAttractionService().hasAttraction(args[1])) {
            sender.sendMessage(ConfigUtil.getMessage("Fastpass.UnknownRide", Collections.singletonMap("%ridename%", args[1])));
            return true;
        }

        nl.iobyte.themepark.api.attraction.objects.Attraction att = nl.iobyte.themepark.ThemePark.getInstance().getAPI().getAttractionService().getAttraction(args[1]);

        Player target;
        if (args.length == 3) {
            target = Bukkit.getPlayer(args[2]);

            if (target == null) {
                sender.sendMessage(ConfigUtil.getMessage("Fastpass.UnknownPlayer", Collections.singletonMap("%playername%", args[2])));
                return true;
            }
        } else {
            target = (Player) sender;
        }

        ItemStack ticket = PlusAPI.getFastpassTicket(att);
        if (ticket == null) return true;
        target.getInventory().addItem(ticket);

        sender.sendMessage(ConfigUtil.getMessage("Fastpass.Given"));
        return true;
    }

    private boolean infoCommand(CommandSender sender) {
        sender.sendMessage("§1==================================");
        sender.sendMessage("§6ThemeParkPlus plugin made by §aSBDeveloper");
        sender.sendMessage("§6Version: " + ThemeParkPlus.getInstance().getDescription().getVersion());
        sender.sendMessage("§6Type /themeparkplus help for more information!");
        sender.sendMessage("§1==================================");
        return true;
    }

    private boolean helpCommand(CommandSender sender) {
        sender.sendMessage("§8ThemeParkPlus commands:");
        sender.sendMessage("§6/themeparkplus info§f: Gives you information about the plugin.");
        sender.sendMessage("§6/themeparkplus help§f: Gives you this help page.");
        sender.sendMessage(" ");
        sender.sendMessage("§6/themeparkplus opengate <World> <X> <Y> <Z> [Player Count/Direction] [Direction]§f: Open a gate!");
        sender.sendMessage("§6/themeparkplus closegate <World> <X> <Y> <Z>§f: Close a gate!");
        sender.sendMessage(" ");
        sender.sendMessage("§6/themeparkplus lampon <World> <X> <Y> <Z> [Seconds on] §f: Turn a lamp on!");
        sender.sendMessage("§6/themeparkplus lampoff <World> <X> <Y> <Z>§f: Turn a lamp off!");
        sender.sendMessage("§6/themeparkplus lampson <World> <X1> <Y1> <Z1> <X2> <Y2> <Z2> [Seconds on]§f: Turn multiple lamps on.");
        sender.sendMessage("§6/themeparkplus lampsoff <World> <X1> <Y1> <Z1> <X2> <Y2> <Z2>§f: Turn multiple lamps off.");
        sender.sendMessage(" ");
        sender.sendMessage("§6/themeparkplus redstonetimer <World> <X> <Y> <Z> <Delay> [Off Delay] §f: Create a Redstone delayer.");
        sender.sendMessage(" ");
        sender.sendMessage("§6/themeparkplus givefpticket <RideID> [Player]§f: Give yourself or someone else a Fastpass ticket (for free).");
        return true;
    }

    private boolean openGate(CommandSender sender, String[] args, int amount, WalkingDirection dir) {
        World bworld = Bukkit.getWorld(args[1]);
        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);
        Location loc = new Location(bworld, bx, by, bz);

        Gate gate = null;
        if (amount != 0 && dir == null) {
            //GEEN ONEWAY MET WEL AANTAL
            gate = new Gate(loc, amount);
        } else if (amount == 0 && dir != null) {
            //ONE WAY ZONDER AANTAL
            gate = new Gate(loc, dir);
        } else if (amount != 0) {
            //WEL ONE WAY MET WEL AANTAL
            gate = new Gate(loc, amount, dir);
        }

        if (gate != null) {
            PlusAPI.addGate(gate);
            ThemeParkPlus.getData().save();
        }

        Block b = loc.getBlock();
        if (LGUtil.isOpenable(b)) {
            if (LGUtil.isOpen(b)) {
                sender.sendMessage(ConfigUtil.getMessage("Gates.AlreadyOpen"));
                return true;
            }
            if (gate != null && gate.isDirectional()) {
                LGUtil.openGate(b, gate.getDirection().getBlockFace());
            } else {
                LGUtil.openGate(b);
            }
        } else {
            sender.sendMessage(ConfigUtil.getMessage("Gates.NoGate"));
            return true;
        }

        if (amount == 0) {
            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Gates.Opened"));
        } else {
            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Gates.OpenedAmount", Collections.singletonMap("%amount%", String.valueOf(amount))));
        }
        return true;
    }

    private boolean closeGate(CommandSender sender, String[] args) {
        World bworld = Bukkit.getWorld(args[1]);
        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);
        Location loc = new Location(bworld, bx, by, bz);

        Block b = loc.getBlock();
        if (LGUtil.isOpenable(b)) {
            Gate gate = PlusAPI.getGate(loc);
            if (gate != null) {
                PlusAPI.removeGate(gate);
                ThemeParkPlus.getData().save();
            }

            if (!LGUtil.isOpen(b)) {
                sender.sendMessage(ConfigUtil.getMessage("Gates.AlreadyClosed"));
                return true;
            }
            LGUtil.closeGate(b);
        } else {
            sender.sendMessage(ConfigUtil.getMessage("Gates.NoGate"));
            return true;
        }

        if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Gates.Closed"));
        return true;
    }

    private boolean lampTurnOnCommand(CommandSender sender, String[] args, int secOn) {
        World bworld = Bukkit.getWorld(args[1]);
        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);
        Location loc = new Location(bworld, bx, by, bz);
        Block block = loc.getBlock();

        if (secOn == 0) {
            if (!LGUtil.zetLampAan(block)) {
                sender.sendMessage(ConfigUtil.getMessage("Lamp.ErrorOn"));
                return true;
            }

            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamp.TurnedOn"));
        } else {
            if (!LGUtil.zetLampAan(block)) {
                sender.sendMessage(ConfigUtil.getMessage("Lamp.ErrorOn"));
                return true;
            }

            Bukkit.getScheduler().runTaskLater(ThemeParkPlus.getInstance(), () -> LGUtil.zetLampUit(block), secOn * 20L);
            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamp.TurnedOnSec", Collections.singletonMap("%sec%", String.valueOf(secOn))));
        }
        return true;
    }

    private boolean lampTurnOffCommand(CommandSender sender, String[] args) {
        World bworld = Bukkit.getWorld(args[1]);
        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);
        Location loc = new Location(bworld, bx, by, bz);

        if (!LGUtil.zetLampUit(loc.getBlock())) {
            sender.sendMessage(ConfigUtil.getMessage("Lamp.ErrorOff"));
            return true;
        }

        if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamp.TurnedOff"));
        return true;
    }

    private boolean lampsTurnOnCommand(CommandSender sender, String[] args, int secOn) {
        World bworld = Bukkit.getWorld(args[1]);

        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);

        double bx2 = Double.parseDouble(args[5]);
        double by2 = Double.parseDouble(args[6]);
        double bz2 = Double.parseDouble(args[7]);

        Location loc = new Location(bworld, bx, by, bz);
        Location loc2 = new Location(bworld, bx2, by2, bz2);

        Cuboid cub = new Cuboid(loc, loc2);

        if (secOn == 0) {
            Bukkit.getScheduler().runTaskAsynchronously(ThemeParkPlus.getInstance(), () -> cub.getBlocks().forEach(block -> {
                if (block.getType().name().contains("REDSTONE_LAMP")) Bukkit.getScheduler().runTask(ThemeParkPlus.getInstance(), () -> LGUtil.zetLampAan(block));
            }));
            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamps.TurnedOn"));
        } else {
            Bukkit.getScheduler().runTaskAsynchronously(ThemeParkPlus.getInstance(), () -> cub.getBlocks().forEach(block -> {
                if (block.getType().name().contains("REDSTONE_LAMP")) Bukkit.getScheduler().runTask(ThemeParkPlus.getInstance(), () -> LGUtil.zetLampAan(block));
            }));

            Bukkit.getScheduler().runTaskLaterAsynchronously(ThemeParkPlus.getInstance(), () -> cub.getBlocks().forEach(block -> {
                if (block.getType().name().contains("REDSTONE_LAMP")) Bukkit.getScheduler().runTask(ThemeParkPlus.getInstance(), () -> LGUtil.zetLampUit(block));
            }), secOn * 20L);
            if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamps.TurnedOnSec", Collections.singletonMap("%sec%", String.valueOf(secOn))));
        }
        return true;
    }

    private boolean lampsTurnOffCommand(CommandSender sender, String[] args) {
        World bworld = Bukkit.getWorld(args[1]);

        double bx = Double.parseDouble(args[2]);
        double by = Double.parseDouble(args[3]);
        double bz = Double.parseDouble(args[4]);

        double bx2 = Double.parseDouble(args[5]);
        double by2 = Double.parseDouble(args[6]);
        double bz2 = Double.parseDouble(args[7]);

        Location loc = new Location(bworld, bx, by, bz);
        Location loc2 = new Location(bworld, bx2, by2, bz2);

        Cuboid cub = new Cuboid(loc, loc2);

        Bukkit.getScheduler().runTaskAsynchronously(ThemeParkPlus.getInstance(), () -> cub.getBlocks().forEach(block -> {
            if (block.getType().name().contains("REDSTONE_LAMP")) Bukkit.getScheduler().runTask(ThemeParkPlus.getInstance(), () -> LGUtil.zetLampUit(block));
        }));

        if (ConfigUtil.sendConsole(sender)) sender.sendMessage(ConfigUtil.getMessage("Lamps.TurnedOff"));
        return true;
    }

    private static final List<String> COMMANDS = Arrays.asList("info", "help", "opengate", "closegate", "lampon", "lampoff", "lampson", "lampsoff", "redstonetimer", "givefpticket");

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
        //Aliases
        if (label.equalsIgnoreCase("themeparkplus") || label.equalsIgnoreCase("tpp") || label.equalsIgnoreCase("themeparkp")) {
            if (args.length == 1) {
                return StringUtil.copyPartialMatches(args[0], COMMANDS, new ArrayList<>());
            } else if (args.length == 2) {
                if (args[0].equalsIgnoreCase("opengate")
                        || args[0].equalsIgnoreCase("closegate")
                        || args[0].equalsIgnoreCase("lampon")
                        || args[0].equalsIgnoreCase("lampoff")
                        || args[0].equalsIgnoreCase("redstonetimer")) {
                    if (sender instanceof Player) {
                        Player p = (Player) sender;
                        Location targetLoc = getTarget(p).getLocation();
                        return StringUtil.copyPartialMatches(args[1], Collections.singletonList("" + targetLoc.getWorld().getName()), new ArrayList<>());
                    }
                } else if (args[0].equalsIgnoreCase("givefpticket")) {
                    return StringUtil.copyPartialMatches(args[1], ThemePark.getInstance().getAPI().getAttractionService().getAttractions().keySet(), new ArrayList<>());
                }
            } else if (args.length == 3) {
                if (args[0].equalsIgnoreCase("opengate")
                        || args[0].equalsIgnoreCase("closegate")
                        || args[0].equalsIgnoreCase("lampon")
                        || args[0].equalsIgnoreCase("lampoff")
                        || args[0].equalsIgnoreCase("redstonetimer")) {
                    if (sender instanceof Player) {
                        Player p = (Player) sender;
                        Location targetLoc = getTarget(p).getLocation();
                        return StringUtil.copyPartialMatches(args[2], Collections.singletonList("" + targetLoc.getBlockX()), new ArrayList<>());
                    }
                } else if (args[0].equalsIgnoreCase("givefpticket")) {
                    return StringUtil.copyPartialMatches(args[1], Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()), new ArrayList<>());
                }
            } else if (args.length == 4) {
                if (args[0].equalsIgnoreCase("opengate")
                        || args[0].equalsIgnoreCase("closegate")
                        || args[0].equalsIgnoreCase("lampon")
                        || args[0].equalsIgnoreCase("lampoff")
                        || args[0].equalsIgnoreCase("redstonetimer")) {
                    if (sender instanceof Player) {
                        Player p = (Player) sender;
                        Location targetLoc = getTarget(p).getLocation();
                        return StringUtil.copyPartialMatches(args[3], Collections.singletonList("" + targetLoc.getBlockY()), new ArrayList<>());
                    }
                }
            } else if (args.length == 5) {
                if (args[0].equalsIgnoreCase("opengate")
                        || args[0].equalsIgnoreCase("closegate")
                        || args[0].equalsIgnoreCase("lampon")
                        || args[0].equalsIgnoreCase("lampoff")
                        || args[0].equalsIgnoreCase("redstonetimer")) {
                    if (sender instanceof Player) {
                        Player p = (Player) sender;
                        Location targetLoc = getTarget(p).getLocation();
                        return StringUtil.copyPartialMatches(args[4], Collections.singletonList("" + targetLoc.getBlockZ()), new ArrayList<>());
                    }
                }
            } else if (args.length == 6) {
                if (args[0].equalsIgnoreCase("opengate")) {
                    return StringUtil.copyPartialMatches(args[5], Arrays.stream(WalkingDirection.values()).map(Enum::toString).collect(Collectors.toList()), new ArrayList<>());
                }
            } else if (args.length == 7) {
                if (args[0].equalsIgnoreCase("opengate")) {
                    return StringUtil.copyPartialMatches(args[6], Arrays.stream(WalkingDirection.values()).map(Enum::toString).collect(Collectors.toList()), new ArrayList<>());
                }
            }
        }

        return null;
    }

    private Block getTarget(Player p) {
        return p.getTargetBlock(null, 200);
    }
}