package net.viper.status.stats;

import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.viper.status.module.Module;

import java.util.concurrent.TimeUnit;

/**
 * StatsModule: Tracking von Spielerdaten (Playtime, Joins, Kills, Deaths).
 *
 * Fixes:
 *  - BUG-1: Crash-Recovery f\u00fcr currentSessionStart (verhindert falsche Spielzeit nach Absturz)
 *  - BUG-2: kills / deaths werden jetzt getrackt und per POST /stats/update aktualisiert
 */
public class StatsModule implements Module, Listener {

    /**
     * Maximale Sessionl\u00e4nge nach einem Crash noch gutschreiben (24 Stunden).
     * L\u00e4ngere Differenzen sind unrealistisch → werden ignoriert, currentSessionStart = 0 gesetzt.
     */
    private static final long MAX_SESSION_SECONDS = 86_400L;

    private StatsManager manager;
    private StatsStorage storage;

    @Override
    public String getName() {
        return "StatsModule";
    }

    @Override
    public void onEnable(Plugin plugin) {
        manager = new StatsManager();
        storage = new StatsStorage(plugin.getDataFolder());

        try {
            storage.load(manager);
        } catch (Exception e) {
            plugin.getLogger().warning("Fehler beim Laden der Stats: " + e.getMessage());
        }

        // -----------------------------------------------------------------------
        // FIX BUG-1: Crash-Recovery – offene Sessions bereinigen.
        //
        // Bei normalem Shutdown setzt onDisable() currentSessionStart = 0 und speichert.
        // Bei einem Crash (kill -9, OOM, etc.) passiert das nicht. Beim n\u00e4chsten Start
        // sind alle Spieler offline, aber currentSessionStart enth\u00e4lt noch den alten
        // Timestamp. getPlaytimeWithCurrentSession() w\u00fcrde dann f\u00e4lschlicherweise
        // (now - alter_crash_timestamp) zur Spielzeit addieren → massiv falscher Wert.
        //
        // Fix: Nach dem Laden jeden Eintrag pr\u00fcfen. Falls currentSessionStart > 0:
        //   - Plausible Differenz (≤ MAX_SESSION_SECONDS) → als echte Zeit gutschreiben
        //   - Unplausibel (> MAX_SESSION_SECONDS) → verwerfen, nur zur\u00fccksetzen
        //   - In beiden F\u00e4llen: currentSessionStart = 0 setzen
        // -----------------------------------------------------------------------
        long now = System.currentTimeMillis() / 1000L;
        int recovered = 0;
        for (PlayerStats ps : manager.all()) {
            synchronized (ps) {
                if (ps.currentSessionStart > 0) {
                    long delta = now - ps.currentSessionStart;
                    if (delta > 0 && delta <= MAX_SESSION_SECONDS) {
                        ps.totalPlaytime += delta;
                        recovered++;
                    } else if (delta > MAX_SESSION_SECONDS) {
                        plugin.getLogger().warning(
                            "[StatsModule] Unplausibler currentSessionStart f\u00fcr " + ps.name
                            + " (delta=" + delta + "s > " + MAX_SESSION_SECONDS + "s). "
                            + "Session wird ohne Gutschrift zur\u00fcckgesetzt."
                        );
                    }
                    ps.currentSessionStart = 0;
                }
            }
        }
        if (recovered > 0) {
            plugin.getLogger().info(
                "[StatsModule] Crash-Recovery: " + recovered + " offene Session(en) bereinigt und gespeichert."
            );
            try {
                storage.save(manager);
            } catch (Exception e) {
                plugin.getLogger().warning("Fehler beim Speichern nach Crash-Recovery: " + e.getMessage());
            }
        }
        // -----------------------------------------------------------------------

        plugin.getProxy().getPluginManager().registerListener(plugin, this);

        // Auto-Save alle 5 Minuten
        plugin.getProxy().getScheduler().schedule(plugin, () -> {
            try {
                storage.save(manager);
            } catch (Exception e) {
                plugin.getLogger().warning("Fehler beim Auto-Save: " + e.getMessage());
            }
        }, 5, 5, TimeUnit.MINUTES);
    }

    @Override
    public void onDisable(Plugin plugin) {
        if (manager != null && storage != null) {
            long now = System.currentTimeMillis() / 1000L;
            for (PlayerStats ps : manager.all()) {
                synchronized (ps) {
                    if (ps.currentSessionStart > 0) {
                        long delta = now - ps.currentSessionStart;
                        if (delta > 0) ps.totalPlaytime += delta;
                        ps.currentSessionStart = 0;
                    }
                }
            }
            try {
                storage.save(manager);
            } catch (Exception e) {
                plugin.getLogger().warning("Fehler beim Speichern (Shutdown): " + e.getMessage());
            }
        }
    }

    public StatsManager getManager() {
        return manager;
    }

    // --- Events ---

    @EventHandler
    public void onJoin(PostLoginEvent e) {
        try {
            manager.get(e.getPlayer().getUniqueId(), e.getPlayer().getName()).onJoin();
        } catch (Exception ignored) {}
    }

    @EventHandler
    public void onQuit(PlayerDisconnectEvent e) {
        try {
            PlayerStats ps = manager.getIfPresent(e.getPlayer().getUniqueId());
            if (ps != null) ps.onQuit();
        } catch (Exception ignored) {}
    }
}