package com.bitnix.dirtguilds.command;

import com.bitnix.dirtguilds.DirtGuildsPlugin;
import com.bitnix.dirtguilds.guild.Guild;
import com.bitnix.dirtguilds.guild.GuildInvite;
import com.bitnix.dirtguilds.gui.GuildLevelsMenu;
import com.bitnix.dirtguilds.gui.GuildMainMenu;
import com.bitnix.dirtguilds.gui.GuildMembersMenu;
import com.bitnix.dirtguilds.gui.GuildVaultMenu;
import com.bitnix.dirtguilds.util.MessageUtil;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

public class DirtGuildsCommand implements TabExecutor, TabCompleter {

    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.00");

    private final DirtGuildsPlugin plugin;

    public DirtGuildsCommand(DirtGuildsPlugin plugin) {
        this.plugin = plugin;
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!(sender instanceof Player player)) {
            plugin.getMessageUtil().send(sender, "messages.players-only");
            return true;
        }

        if (!player.hasPermission("dirtguilds.use")) {
            plugin.getMessageUtil().send(player, "messages.no-permission");
            return true;
        }

        if (args.length == 0) {
            new GuildMainMenu(plugin).open(player);
            return true;
        }

        String sub = args[0].toLowerCase(Locale.ROOT);
        Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());

        switch (sub) {
            case "create" -> {
                if (guild != null) {
                    plugin.getMessageUtil().send(player, "messages.already-in-guild");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " create <name></red>");
                    return true;
                }

                String name = args[1];
                int min = plugin.getConfig().getInt("settings.min-name-length", 3);
                int max = plugin.getConfig().getInt("settings.max-name-length", 16);

                if (name.length() < min || name.length() > max || !name.matches("[A-Za-z0-9_]+")) {
                    plugin.getMessageUtil().send(player, "messages.invalid-name");
                    return true;
                }

                if (plugin.getGuildManager().guildNameExists(name)) {
                    plugin.getMessageUtil().send(player, "messages.name-taken");
                    return true;
                }

                Guild created = plugin.getGuildManager().createGuild(player.getUniqueId(), name);
                plugin.getMessageUtil().send(player, "messages.guild-created", "%guild%", created.getName());
                return true;
            }

            case "invite" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " invite <player></red>");
                    return true;
                }

                Player target = Bukkit.getPlayerExact(args[1]);
                if (target == null) {
                    plugin.getMessageUtil().send(player, "messages.player-not-found");
                    return true;
                }

                if (plugin.getGuildManager().isInGuild(target.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.target-already-in-guild");
                    return true;
                }

                plugin.getGuildManager().createInvite(target.getUniqueId(), player.getUniqueId(), guild);
                plugin.getMessageUtil().send(player, "messages.invite-sent", "%player%", target.getName());
                plugin.getMessageUtil().send(target, "messages.invite-received", "%guild%", guild.getName());
                return true;
            }

            case "accept", "join" -> {
                if (guild != null) {
                    plugin.getMessageUtil().send(player, "messages.already-in-guild");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " accept <guild></red>");
                    return true;
                }

                Guild targetGuild = plugin.getGuildManager().getGuildByName(args[1]);
                if (targetGuild == null) {
                    plugin.getMessageUtil().send(player, "messages.guild-not-found");
                    return true;
                }

                GuildInvite invite = plugin.getGuildManager().getInvite(player.getUniqueId(), targetGuild.getId());
                if (invite == null) {
                    plugin.getMessageUtil().send(player, "messages.no-invite");
                    return true;
                }

                if (invite.isExpired()) {
                    plugin.getGuildManager().removeInvite(player.getUniqueId(), targetGuild.getId());
                    plugin.getMessageUtil().send(player, "messages.invite-expired");
                    return true;
                }

                plugin.getGuildManager().addMember(targetGuild, player.getUniqueId());
                plugin.getGuildManager().removeInvite(player.getUniqueId(), targetGuild.getId());

                plugin.getMessageUtil().send(player, "messages.joined-guild", "%guild%", targetGuild.getName());

                for (Player online : Bukkit.getOnlinePlayers()) {
                    Guild onlineGuild = plugin.getGuildManager().getGuildByPlayer(online.getUniqueId());
                    if (onlineGuild != null && onlineGuild.getId().equals(targetGuild.getId())) {
                        plugin.getMessageUtil().send(online, "messages.member-joined", "%player%", player.getName());
                    }
                }
                return true;
            }

            case "leave" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getGuildManager().deleteGuild(guild);
                    plugin.getMessageUtil().send(player, "messages.guild-deleted", "%guild%", guild.getName());
                    return true;
                }

                plugin.getGuildManager().removeMember(guild, player.getUniqueId());
                plugin.getMessageUtil().send(player, "messages.left-guild");

                for (Player online : Bukkit.getOnlinePlayers()) {
                    Guild onlineGuild = plugin.getGuildManager().getGuildByPlayer(online.getUniqueId());
                    if (onlineGuild != null && onlineGuild.getId().equals(guild.getId())) {
                        plugin.getMessageUtil().send(online, "messages.member-left", "%player%", player.getName());
                    }
                }
                return true;
            }

            case "rename" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.must-be-owner");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " rename <name></red>");
                    return true;
                }

                String newName = args[1];
                int min = plugin.getConfig().getInt("settings.min-name-length", 3);
                int max = plugin.getConfig().getInt("settings.max-name-length", 16);

                if (newName.length() < min || newName.length() > max || !newName.matches("[A-Za-z0-9_]+")) {
                    plugin.getMessageUtil().send(player, "messages.invalid-name");
                    return true;
                }

                if (!guild.getName().equalsIgnoreCase(newName) && plugin.getGuildManager().guildNameExists(newName)) {
                    plugin.getMessageUtil().send(player, "messages.name-taken");
                    return true;
                }

                plugin.getGuildManager().renameGuild(guild, newName);
                plugin.getMessageUtil().send(player, "messages.guild-renamed", "%guild%", newName);
                return true;
            }

            case "prefix" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " prefix <text></red>");
                    return true;
                }

                String prefix = args[1];
                plugin.getGuildManager().setPrefix(guild, prefix);
                plugin.getMessageUtil().send(player, "messages.prefix-updated");
                return true;
            }

            case "ff", "friendlyfire", "pvp" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                boolean enabled = !guild.isFriendlyFire();
                plugin.getGuildManager().setFriendlyFire(guild, enabled);

                if (enabled) {
                    plugin.getMessageUtil().send(player, "messages.friendly-fire-enabled");
                } else {
                    plugin.getMessageUtil().send(player, "messages.friendly-fire-disabled");
                }
                return true;
            }

            case "chat" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                boolean enabled = !plugin.getGuildManager().isGuildChatEnabled(player.getUniqueId());
                plugin.getGuildManager().setGuildChatEnabled(player.getUniqueId(), enabled);

                if (enabled) {
                    plugin.getMessageUtil().send(player, "messages.guild-chat-enabled");
                } else {
                    plugin.getMessageUtil().send(player, "messages.guild-chat-disabled");
                }
                return true;
            }

            case "bank" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (args.length == 1) {
                    player.sendMessage(MessageUtil.mm(plugin.getMessageUtil().format("messages.prefix") + "<yellow>Use /guild bank deposit <amount> or /guild bank withdraw <amount></yellow>"));
                    return true;
                }

                if (args.length < 3) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " bank <deposit|withdraw> <amount></red>");
                    return true;
                }

                String action = args[1].toLowerCase(Locale.ROOT);
                double amount;

                try {
                    amount = Double.parseDouble(args[2]);
                } catch (NumberFormatException exception) {
                    plugin.getMessageUtil().send(player, "messages.bank-invalid-amount");
                    return true;
                }

                if (amount <= 0) {
                    plugin.getMessageUtil().send(player, "messages.bank-invalid-amount");
                    return true;
                }

                boolean useVault = plugin.getConfig().getBoolean("settings.bank.use-vault", true);

                if (action.equals("deposit")) {
                    if (useVault) {
                        if (!plugin.getEconomyHook().isAvailable()) {
                            plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
                            return true;
                        }

                        if (plugin.getEconomyHook().getEconomy().getBalance(player) < amount) {
                            plugin.getMessageUtil().send(player, "messages.bank-player-insufficient-funds");
                            return true;
                        }

                        EconomyResponse response = plugin.getEconomyHook().getEconomy().withdrawPlayer(player, amount);
                        if (!response.transactionSuccess()) {
                            plugin.getMessageUtil().send(player, "messages.bank-player-insufficient-funds");
                            return true;
                        }
                    }

                    plugin.getGuildManager().deposit(guild, amount);
                    plugin.getMessageUtil().send(player, "messages.bank-deposit", "%amount%", DECIMAL_FORMAT.format(amount));
                    return true;
                }

                if (action.equals("withdraw")) {
                    if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                        plugin.getMessageUtil().send(player, "messages.no-permission");
                        return true;
                    }

                    if (!plugin.getGuildManager().withdraw(guild, amount)) {
                        plugin.getMessageUtil().send(player, "messages.bank-insufficient-funds");
                        return true;
                    }

                    if (useVault) {
                        if (!plugin.getEconomyHook().isAvailable()) {
                            plugin.getGuildManager().deposit(guild, amount);
                            plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
                            return true;
                        }

                        EconomyResponse response = plugin.getEconomyHook().getEconomy().depositPlayer(player, amount);
                        if (!response.transactionSuccess()) {
                            plugin.getGuildManager().deposit(guild, amount);
                            plugin.getMessageUtil().send(player, "messages.bank-vault-unavailable");
                            return true;
                        }
                    }

                    plugin.getMessageUtil().send(player, "messages.bank-withdraw", "%amount%", DECIMAL_FORMAT.format(amount));
                    return true;
                }

                plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " bank <deposit|withdraw> <amount></red>");
                return true;
            }

            case "vault" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                new GuildVaultMenu(plugin).open(player, guild);
                return true;
            }

            case "levels", "level" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                new GuildLevelsMenu(plugin).open(player, guild);
                return true;
            }

            case "members" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                new GuildMembersMenu(plugin).open(player, guild);
                return true;
            }

            case "promote" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " promote <player></red>");
                    return true;
                }

                OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
                UUID targetUuid = target.getUniqueId();

                if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
                    plugin.getMessageUtil().send(player, "messages.player-not-found");
                    return true;
                }

                String current = guild.getRank(targetUuid).toUpperCase();
                String next = switch (current) {
                    case "MEMBER" -> "MOD";
                    case "MOD" -> "ADMIN";
                    default -> "ADMIN";
                };

                plugin.getGuildManager().setMemberRank(guild, targetUuid, next);
                String name = target.getName() == null ? targetUuid.toString() : target.getName();
                plugin.getMessageUtil().send(player, "messages.rank-updated", "%player%", name, "%rank%", next);
                return true;
            }

            case "demote" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " demote <player></red>");
                    return true;
                }

                OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
                UUID targetUuid = target.getUniqueId();

                if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
                    plugin.getMessageUtil().send(player, "messages.player-not-found");
                    return true;
                }

                String current = guild.getRank(targetUuid).toUpperCase();
                String next = switch (current) {
                    case "ADMIN" -> "MOD";
                    case "MOD" -> "MEMBER";
                    default -> "MEMBER";
                };

                plugin.getGuildManager().setMemberRank(guild, targetUuid, next);
                String name = target.getName() == null ? targetUuid.toString() : target.getName();
                plugin.getMessageUtil().send(player, "messages.rank-updated", "%player%", name, "%rank%", next);
                return true;
            }

            case "kick" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " kick <player></red>");
                    return true;
                }

                OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
                UUID targetUuid = target.getUniqueId();

                if (!guild.isMember(targetUuid) || targetUuid.equals(guild.getOwner())) {
                    plugin.getMessageUtil().send(player, "messages.player-not-found");
                    return true;
                }

                plugin.getGuildManager().removeMember(guild, targetUuid);
                String name = target.getName() == null ? targetUuid.toString() : target.getName();
                plugin.getMessageUtil().sendRaw(player, plugin.getMessageUtil().format("messages.prefix") + "<red>Kicked <yellow>" + name + "</yellow> from the guild.</red>");
                return true;
            }

            case "tp" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " tp <player></red>");
                    return true;
                }

                Player target = Bukkit.getPlayerExact(args[1]);
                if (target == null || !guild.isMember(target.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.teleport-offline");
                    return true;
                }

                player.teleport(target.getLocation());
                plugin.getMessageUtil().send(player, "messages.teleport-success", "%player%", target.getName());
                return true;
            }

            case "inv", "inventory" -> {
                if (guild == null) {
                    plugin.getMessageUtil().send(player, "messages.not-in-guild");
                    return true;
                }

                if (!guild.canManage(player.getUniqueId()) && !guild.getOwner().equals(player.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                if (args.length < 2) {
                    plugin.getMessageUtil().sendRaw(player, "<red>Usage: /" + label + " inventory <player></red>");
                    return true;
                }

                Player target = Bukkit.getPlayerExact(args[1]);
                if (target == null || !guild.isMember(target.getUniqueId())) {
                    plugin.getMessageUtil().send(player, "messages.player-not-found");
                    return true;
                }

                Inventory copy = Bukkit.createInventory(null, 54, MessageUtil.mm("<gold>Inventory: " + target.getName() + "</gold>"));
                copy.setContents(target.getInventory().getContents());
                player.openInventory(copy);
                plugin.getMessageUtil().send(player, "messages.inventory-opened", "%player%", target.getName());
                return true;
            }

            case "reload" -> {
                if (!player.hasPermission("dirtguilds.admin")) {
                    plugin.getMessageUtil().send(player, "messages.no-permission");
                    return true;
                }

                plugin.reloadConfig();
                plugin.getMessageUtil().send(player, "messages.reload");
                return true;
            }

            default -> {
                plugin.getMessageUtil().send(player, "messages.unknown-command");
                return true;
            }
        }
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
        List<String> suggestions = new ArrayList<>();

        if (args.length == 1) {
            suggestions.add("create");
            suggestions.add("invite");
            suggestions.add("accept");
            suggestions.add("join");
            suggestions.add("leave");
            suggestions.add("rename");
            suggestions.add("prefix");
            suggestions.add("chat");
            suggestions.add("pvp");
            suggestions.add("bank");
            suggestions.add("vault");
            suggestions.add("level");
            suggestions.add("members");
            suggestions.add("promote");
            suggestions.add("demote");
            suggestions.add("kick");
            suggestions.add("tp");
            suggestions.add("inventory");
            suggestions.add("reload");
            return filter(suggestions, args[0]);
        }

        if (args.length == 2 && args[0].equalsIgnoreCase("invite")) {
            for (Player player : Bukkit.getOnlinePlayers()) {
                suggestions.add(player.getName());
            }
            return filter(suggestions, args[1]);
        }

        if (args.length == 2 && (args[0].equalsIgnoreCase("accept") || args[0].equalsIgnoreCase("join"))) {
            for (Guild guild : plugin.getGuildManager().getAllGuilds()) {
                suggestions.add(guild.getName());
            }
            return filter(suggestions, args[1]);
        }

        if (args.length == 2 && args[0].equalsIgnoreCase("bank")) {
            suggestions.add("deposit");
            suggestions.add("withdraw");
            return filter(suggestions, args[1]);
        }

        if (args.length == 3 && args[0].equalsIgnoreCase("bank")) {
            suggestions.add("100");
            suggestions.add("500");
            suggestions.add("1000");
            suggestions.add("5000");
            return filter(suggestions, args[2]);
        }

        if (args.length == 2 && isMemberTargetSubcommand(args[0]) && sender instanceof Player player) {
            Guild guild = plugin.getGuildManager().getGuildByPlayer(player.getUniqueId());
            if (guild != null) {
                for (UUID uuid : guild.getMemberRanks().keySet()) {
                    OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
                    if (offlinePlayer.getName() != null) {
                        suggestions.add(offlinePlayer.getName());
                    }
                }
            }
            return filter(suggestions, args[1]);
        }

        return suggestions;
    }

    private boolean isMemberTargetSubcommand(String input) {
        String lower = input.toLowerCase(Locale.ROOT);
        return lower.equals("promote")
                || lower.equals("demote")
                || lower.equals("kick")
                || lower.equals("tp")
                || lower.equals("inventory")
                || lower.equals("inv");
    }

    private List<String> filter(List<String> input, String token) {
        String lower = token.toLowerCase(Locale.ROOT);
        return input.stream()
                .filter(value -> value.toLowerCase(Locale.ROOT).startsWith(lower))
                .distinct()
                .sorted()
                .toList();
    }
}
