package de.simolzimol.mclogger.consent.commands;

import de.simolzimol.mclogger.consent.ConsentConfig;
import de.simolzimol.mclogger.consent.ConsentPlugin;
import de.simolzimol.mclogger.consent.util.MessageUtil;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.*;
import org.bukkit.entity.Player;

import java.util.*;

/**
 * Handles the {@code /consent} command and its sub-commands.
 *
 * <p><b>Player sub-commands:</b>
 * <ul>
 *   <li>{@code /consent accept}   – record consent for the current policy version</li>
 *   <li>{@code /consent decline}  – record decline and kick the player</li>
 *   <li>{@code /consent withdraw} – withdraw previously given consent (Art. 7(3) GDPR)</li>
 *   <li>{@code /consent status}   – show current consent status</li>
 *   <li>{@code /consent help}     – show help text</li>
 * </ul>
 *
 * <p><b>Admin sub-commands} (require {@code mclogger.consent.admin}):</b>
 * <ul>
 *   <li>{@code /consent admin reload}              – reload plugin config</li>
 *   <li>{@code /consent admin list}                – list online players who haven't consented</li>
 *   <li>{@code /consent admin revoke <player>}     – revoke all consents for a player</li>
 *   <li>{@code /consent admin forceaccept <player>} – record consent on behalf of a player</li>
 * </ul>
 */
public class ConsentCommand implements CommandExecutor, TabCompleter {

    private static final MiniMessage MM = MiniMessage.miniMessage();

    private final ConsentPlugin plugin;

    public ConsentCommand(ConsentPlugin plugin) {
        this.plugin = plugin;
    }

    // ─────────────────────────────────────────────────────────
    // Command dispatch
    // ─────────────────────────────────────────────────────────

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        ConsentConfig cfg = plugin.getConsentConfig();

        if (args.length == 0) {
            sendHelp(sender, cfg);
            return true;
        }

        switch (args[0].toLowerCase(Locale.ROOT)) {
            case "accept"   -> handleAccept(sender, cfg);
            case "decline"  -> handleDecline(sender, cfg);
            case "withdraw" -> handleWithdraw(sender, cfg);
            case "status"   -> handleStatus(sender, cfg);
            case "help"     -> sendHelp(sender, cfg);
            case "admin" -> {
                if (!sender.hasPermission("mclogger.consent.admin")) {
                    sender.sendMessage(MM.deserialize(cfg.getMsgNoPermission()));
                    return true;
                }
                handleAdmin(sender, cfg, args);
            }
            default -> sendHelp(sender, cfg);
        }
        return true;
    }

    // ─────────────────────────────────────────────────────────
    // Player sub-commands
    // ─────────────────────────────────────────────────────────

    private void handleAccept(CommandSender sender, ConsentConfig cfg) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage(MM.deserialize("<red>This command can only be used in-game."));
            return;
        }

        String uuid    = player.getUniqueId().toString();
        String version = cfg.getPolicyVersion();
        String ip      = player.getAddress() != null
                ? player.getAddress().getAddress().getHostAddress() : "unknown";

        plugin.getConsentDatabase().recordConsent(uuid, player.getName(), version, ip);
        plugin.getPendingConsent().remove(player.getUniqueId());

        String msg = MessageUtil.replace(cfg.getMsgAccepted(), player, cfg);
        player.sendMessage(MM.deserialize(msg));

        // Notify admins if configured
        if (cfg.isNotifyAdminsOnConsent()) {
            String notify = MessageUtil.replace(cfg.getMsgAdminNotifyConsent(), player, cfg);
            broadcastAdmins(notify);
        }
    }

    private void handleDecline(CommandSender sender, ConsentConfig cfg) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage(MM.deserialize("<red>This command can only be used in-game."));
            return;
        }

        String uuid    = player.getUniqueId().toString();
        String version = cfg.getPolicyVersion();
        String ip      = player.getAddress() != null
                ? player.getAddress().getAddress().getHostAddress() : "unknown";

        plugin.getConsentDatabase().recordDecline(uuid, player.getName(), version, ip);
        plugin.getPendingConsent().remove(player.getUniqueId());

        String msg = MessageUtil.replace(cfg.getMsgDeclined(), player, cfg);
        plugin.networkKick(player, MM.deserialize(msg));
    }

    private void handleWithdraw(CommandSender sender, ConsentConfig cfg) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage(MM.deserialize("<red>This command can only be used in-game."));
            return;
        }

        String uuid    = player.getUniqueId().toString();
        String version = cfg.getPolicyVersion();

        // Revoke all versions so the player must re-accept next time
        plugin.getConsentDatabase().revokeConsent(uuid, null);
        plugin.getPendingConsent().remove(player.getUniqueId());

        // Record the withdrawal in the decline table as an audit trail
        plugin.getConsentDatabase().recordDecline(uuid, player.getName(), version + "_WITHDRAWN",
                player.getAddress() != null ? player.getAddress().getAddress().getHostAddress() : "unknown");

        String withdrawMsg = MessageUtil.replace(cfg.getMsgWithdrawn(), player, cfg);
        player.sendMessage(MM.deserialize(withdrawMsg));

        // Kick using the configured kick message
        String kickMsg = MessageUtil.replace(cfg.getMsgWithdrawKick(), player, cfg);
        plugin.networkKick(player, MM.deserialize(kickMsg));
    }

    private void handleStatus(CommandSender sender, ConsentConfig cfg) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage(MM.deserialize("<red>This command can only be used in-game."));
            return;
        }

        boolean consented = plugin.getConsentDatabase()
                .hasConsented(player.getUniqueId().toString(), cfg.getPolicyVersion());

        String template = consented ? cfg.getMsgStatusAccepted() : cfg.getMsgStatusPending();
        player.sendMessage(MM.deserialize(MessageUtil.replace(template, player, cfg)));
    }

    private void sendHelp(CommandSender sender, ConsentConfig cfg) {
        for (String line : cfg.getHelpLines()) {
            if (sender instanceof Player player) {
                sender.sendMessage(MM.deserialize(MessageUtil.replace(line, player, cfg)));
            } else {
                // Strip MiniMessage tags for console
                sender.sendMessage(MM.stripTags(line));
            }
        }
    }

    // ─────────────────────────────────────────────────────────
    // Admin sub-commands
    // ─────────────────────────────────────────────────────────

    private void handleAdmin(CommandSender sender, ConsentConfig cfg, String[] args) {
        if (args.length < 2) {
            sender.sendMessage(MM.deserialize("<yellow>Usage: /consent admin <reload|list|revoke|forceaccept>"));
            return;
        }

        switch (args[1].toLowerCase(Locale.ROOT)) {
            case "reload" -> {
                plugin.reloadConsentConfig();
                sender.sendMessage(MM.deserialize("<green>[MCConsent] Config reloaded."));
            }

            case "list" -> {
                Set<UUID> pending = plugin.getPendingConsent();
                if (pending.isEmpty()) {
                    sender.sendMessage(MM.deserialize("<green>[MCConsent] No players are currently awaiting consent."));
                    return;
                }
                sender.sendMessage(MM.deserialize("<yellow>[MCConsent] Pending consent (" + pending.size() + "):"));
                for (UUID uuid : pending) {
                    Player p = Bukkit.getPlayer(uuid);
                    String name = p != null ? p.getName() : uuid.toString();
                    sender.sendMessage(MM.deserialize("<gray>  - " + name));
                }
            }

            case "revoke" -> {
                if (args.length < 3) {
                    sender.sendMessage(MM.deserialize("<red>Usage: /consent admin revoke <player>"));
                    return;
                }
                String target = args[2];
                @SuppressWarnings("deprecation")
                OfflinePlayer op = Bukkit.getOfflinePlayer(target);
                if (!op.hasPlayedBefore() && !op.isOnline()) {
                    sender.sendMessage(MM.deserialize("<red>Player not found: " + target));
                    return;
                }
                plugin.getConsentDatabase().revokeConsent(op.getUniqueId().toString(), null);
                // Also add to pending if currently online
                if (op.isOnline()) {
                    plugin.getPendingConsent().add(op.getUniqueId());
                }
                sender.sendMessage(MM.deserialize("<green>[MCConsent] Consent revoked for " + op.getName()
                        + ". They will need to re-accept on next login."));
            }

            case "forceaccept" -> {
                if (args.length < 3) {
                    sender.sendMessage(MM.deserialize("<red>Usage: /consent admin forceaccept <player>"));
                    return;
                }
                String target = args[2];
                @SuppressWarnings("deprecation")
                OfflinePlayer op = Bukkit.getOfflinePlayer(target);
                if (!op.hasPlayedBefore() && !op.isOnline()) {
                    sender.sendMessage(MM.deserialize("<red>Player not found: " + target));
                    return;
                }
                String version = cfg.getPolicyVersion();
                plugin.getConsentDatabase().recordConsent(
                        op.getUniqueId().toString(),
                        op.getName() != null ? op.getName() : "unknown",
                        version, "admin-forced");
                plugin.getPendingConsent().remove(op.getUniqueId());
                sender.sendMessage(MM.deserialize("<green>[MCConsent] Consent recorded for " + op.getName()
                        + " (version: " + version + ")."));
            }

            default -> sender.sendMessage(MM.deserialize(
                    "<yellow>Unknown admin sub-command. Use: reload | list | revoke | forceaccept"));
        }
    }

    // ─────────────────────────────────────────────────────────
    // Tab completion
    // ─────────────────────────────────────────────────────────

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
        if (args.length == 1) {
            List<String> base = new ArrayList<>(List.of("accept", "decline", "withdraw", "status", "help"));
            if (sender.hasPermission("mclogger.consent.admin")) base.add("admin");
            return filter(base, args[0]);
        }
        if (args.length == 2 && args[0].equalsIgnoreCase("admin")
                && sender.hasPermission("mclogger.consent.admin")) {
            return filter(List.of("reload", "list", "revoke", "forceaccept"), args[1]);
        }
        if (args.length == 3
                && args[0].equalsIgnoreCase("admin")
                && (args[1].equalsIgnoreCase("revoke") || args[1].equalsIgnoreCase("forceaccept"))
                && sender.hasPermission("mclogger.consent.admin")) {
            List<String> names = new ArrayList<>();
            Bukkit.getOnlinePlayers().forEach(p -> names.add(p.getName()));
            return filter(names, args[2]);
        }
        return Collections.emptyList();
    }

    // ─────────────────────────────────────────────────────────
    // Helpers
    // ─────────────────────────────────────────────────────────

    private static List<String> filter(List<String> options, String prefix) {
        List<String> result = new ArrayList<>();
        String lower = prefix.toLowerCase(Locale.ROOT);
        for (String opt : options) {
            if (opt.toLowerCase(Locale.ROOT).startsWith(lower)) result.add(opt);
        }
        return result;
    }

    private void broadcastAdmins(String miniMsg) {
        Bukkit.getOnlinePlayers().stream()
                .filter(p -> p.hasPermission("mclogger.consent.admin"))
                .forEach(p -> p.sendMessage(MM.deserialize(miniMsg)));
    }
}
