package de.simolzimol.mclogger.consent;

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;

import de.simolzimol.mclogger.consent.commands.ConsentCommand;
import de.simolzimol.mclogger.consent.database.ConsentDatabase;
import de.simolzimol.mclogger.consent.listeners.ConsentListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;

/**
 * MCConsent – Privacy Policy consent enforcement for Paper servers.
 *
 * <p>Key components:
 * <ul>
 *   <li>{@link ConsentConfig}   – Strongly-typed configuration wrapper</li>
 *   <li>{@link ConsentDatabase} – HikariCP-backed MariaDB/MySQL client</li>
 *   <li>{@link ConsentListener}  – Enforces consent on login/join/move/chat</li>
 *   <li>{@link ConsentCommand}  – /consent accept|decline|status|admin …</li>
 * </ul>
 *
 * @author SimolZimol
 * @version 1.0.0
 */
public class ConsentPlugin extends JavaPlugin {

    private static ConsentPlugin instance;

    private ConsentConfig  consentConfig;
    private ConsentDatabase consentDatabase;

    /**
     * UUIDs of players who have joined but not yet consented (HOLD mode).
     * Thread-safe because both the main thread and async events touch this set.
     */
    private final Set<UUID> pendingConsent = ConcurrentHashMap.newKeySet();

    // ─────────────────────────────────────────────────────────
    @Override
    public void onEnable() {
        instance = this;

        saveDefaultConfig();
        consentConfig = new ConsentConfig(this);

        if (!consentConfig.isEnabled()) {
            getLogger().info("[MCConsent] Plugin is disabled via config (enabled: false).");
            return;
        }

        // Connect to database
        consentDatabase = new ConsentDatabase(this);
        if (!consentDatabase.connect()) {
            getLogger().severe("[MCConsent] Could not connect to the database – disabling plugin.");
            getServer().getPluginManager().disablePlugin(this);
            return;
        }

        // Register plugin-messaging channel for Velocity / BungeeCord network kick
        getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");

        // Register event listeners
        ConsentListener listener = new ConsentListener(this);
        getServer().getPluginManager().registerEvents(listener, this);

        // Register /consent command
        ConsentCommand cmdHandler = new ConsentCommand(this);
        PluginCommand cmd = getCommand("consent");
        if (cmd != null) {
            cmd.setExecutor(cmdHandler);
            cmd.setTabCompleter(cmdHandler);
        } else {
            getLogger().warning("[MCConsent] Could not find 'consent' command in plugin.yml!");
        }

        getLogger().info("[MCConsent] Started. Mode=" + consentConfig.getEnforcementMode()
                + "  PolicyVersion=" + consentConfig.getPolicyVersion());

        // GDPR Art. 5(1)(e) – automatic retention purge, runs once every 24 h
        int consentDays = consentConfig.getConsentRetentionDays();
        int declineDays = consentConfig.getDeclineRetentionDays();
        if (consentDays > 0 || declineDays > 0) {
            long dayTicks = 20L * 60 * 60 * 24;
            getServer().getScheduler().runTaskTimerAsynchronously(this,
                () -> consentDatabase.purgeOldConsents(consentDays, declineDays),
                dayTicks, dayTicks);
            getLogger().info("[MCConsent] GDPR retention purge scheduled (consent=" + consentDays
                    + "d, declines=" + declineDays + "d).");
        }
    }

    // ─────────────────────────────────────────────────────────
    @Override
    public void onDisable() {
        pendingConsent.clear();
        if (consentDatabase != null) {
            consentDatabase.disconnect();
        }
        getLogger().info("[MCConsent] Disabled.");
    }

    // ─────────────────────────────────────────────────────────
    // Accessors
    // ─────────────────────────────────────────────────────────

    public static ConsentPlugin getInstance() {
        return instance;
    }

    public ConsentConfig getConsentConfig() {
        return consentConfig;
    }

    public ConsentDatabase getConsentDatabase() {
        return consentDatabase;
    }

    /** Live set of players currently waiting for consent (HOLD mode). */
    public Set<UUID> getPendingConsent() {
        return pendingConsent;
    }

    /**
     * Kicks a player from the current server, or from the entire
     * Velocity / BungeeCord network when {@code velocity-kick: true} is set
     * in the config.  Falls back to a normal kick if the proxy message fails.
     */
    public void networkKick(Player player, Component reason) {
        if (consentConfig.isVelocityKick()) {
            try {
                ByteArrayDataOutput out = ByteStreams.newDataOutput();
                out.writeUTF("KickPlayer");
                out.writeUTF(player.getName());
                out.writeUTF(LegacyComponentSerializer.legacySection().serialize(reason));
                player.sendPluginMessage(this, "BungeeCord", out.toByteArray());
            } catch (Exception e) {
                getLogger().warning("[MCConsent] Velocity/BungeeCord network kick failed: " + e.getMessage());
                player.kick(reason);
                return;
            }
            // Fallback: if the proxy never kicks, remove the player after 1 s
            getServer().getScheduler().runTaskLater(this, () -> {
                if (player.isOnline()) player.kick(reason);
            }, 20L);
        } else {
            player.kick(reason);
        }
    }
}
