/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.lobby.jumpandrun;

import de.steamwar.linkage.Linked;
import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.listener.PlayerSpawn;
import de.steamwar.lobby.util.LeaderboardManager;
import de.steamwar.sql.SteamwarUser;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;

import java.text.SimpleDateFormat;
import java.util.*;

@Linked
public class JumpAndRun implements Listener {

    public static final String JUMP_AND_RUN_CONFIG = "jump_and_run@winter25";

    private static final String BAR_EMPTY = "||||||||||||||||||||||||||||||";

    public static List<Vector> points = new ArrayList<>();
    public static List<Vector> actualPoints = new ArrayList<>();

    private static final Map<Player, Integer> CURRENT_POS = new HashMap<>();
    private static final Map<Player, Integer> FAILS = new HashMap<>();
    private static final Map<Player, Long> START = new HashMap<>();
    private static final Map<Player, Long> CLICKED = new HashMap<>();
    private static final Map<Player, Integer> CLICKED_COUNT = new HashMap<>();

    private static final LeaderboardManager LEADERBOARD = new LeaderboardManager(LobbySystem.getEntityServer(false), JUMP_AND_RUN_CONFIG, new Location(Bukkit.getWorlds().get(0), 2338.5, 42.5, 1231.5));

    {
        Bukkit.getScheduler().runTaskTimer(LobbySystem.getInstance(), () -> {
            Set<Player> toReset = new HashSet<>();
            CURRENT_POS.forEach((player, index) -> {
                if (System.currentTimeMillis() - CLICKED.getOrDefault(player, 0L) > 500) {
                    CLICKED.remove(player);
                    CLICKED_COUNT.remove(player);
                } else {
                    CLICKED_COUNT.put(player, CLICKED_COUNT.getOrDefault(player, -1) + 1);
                }

                Location location = player.getLocation();
                Vector point = points.get(index);
                if (index < points.size() - 1) {
                    Vector nextPoint = points.get(index + 1);
                    double y = Math.min(point.getY(), nextPoint.getY()) - 2;
                    if (location.getY() < y) {
                        location.setX(point.getX());
                        location.setY(point.getY());
                        location.setZ(point.getZ());
                        player.teleport(location);
                        player.playSound(location, Sound.ENTITY_ENDERMAN_TELEPORT, 1, 1);
                        FAILS.put(player, FAILS.getOrDefault(player, 0) + 1);
                    }
                }

                int count = CLICKED_COUNT.getOrDefault(player, -1);
                if (count >= 0) {
                    if (count > 20) {
                        toReset.add(player);
                        return;
                    }
                    count = Math.min(count / 2, 30);
                    LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_CANCEL", player, ChatMessageType.ACTION_BAR, "§e" + BAR_EMPTY.substring(0, count) + "§7" + BAR_EMPTY.substring(count));
                } else {
                    long time = System.currentTimeMillis() - START.get(player);
                    SimpleDateFormat format = new SimpleDateFormat(LobbySystem.getMessage().parse("JUMP_AND_RUN_TIME", player), Locale.ROOT);
                    String parsed = format.format(new Date(time));
                    LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_PROGRESS", player, ChatMessageType.ACTION_BAR, index + 1, points.size(), FAILS.get(player), parsed);
                }
            });
            toReset.forEach(player -> {
                reset(player);
                player.teleport(Bukkit.getWorlds().get(0).getSpawnLocation().clone().add(0.5, 0, 0.5));
            });
        }, 1, 1);
    }

    public static boolean isPlayerInJumpAndRun(Player player) {
        return CURRENT_POS.containsKey(player);
    }

    @EventHandler
    public void onPlayerMove(PlayerMoveEvent event) {
        Location location = event.getTo();
        Location checkLocation = location.clone();
        checkLocation.setY(checkLocation.getY() - 0.1);
        if (checkLocation.getBlock().getType() == Material.AIR) {
            return;
        }
        int index = CURRENT_POS.getOrDefault(event.getPlayer(), -1) + 1;
        if (index >= points.size()) {
            return;
        }
        Vector point = points.get(index);
        if (location.getY() < point.getY()) index = -2;
        if (location.toVector().distanceSquared(point) >= 17) index = -2;
        if (index > 0 && index < points.size() - 1) {
            Vector nextPoint = points.get(index + 1);
            if (location.getY() >= nextPoint.getY() && location.toVector().distanceSquared(nextPoint) < 17) {
                index = index + 1;
            }
        }
        if (index == -2) {
            return;
        }

        CURRENT_POS.put(event.getPlayer(), index);
        event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.4F, 1);
        if (index < points.size() - 1) {
            event.getPlayer().setCompassTarget(points.get(index + 1).toLocation(location.getWorld()));
        }
        if (index == 0) {
            event.getPlayer().setAllowFlight(false);
            event.getPlayer().getInventory().clear();
            event.getPlayer().updateInventory();
            event.getPlayer().getInventory().setItem(4, new ItemStack(Material.COMPASS, 1));
            FAILS.put(event.getPlayer(), 0);
            START.put(event.getPlayer(), System.currentTimeMillis());
        }
        if (index == points.size() - 1) {
            long time = System.currentTimeMillis() - START.get(event.getPlayer());
            SimpleDateFormat format = new SimpleDateFormat(LobbySystem.getMessage().parse("JUMP_AND_RUN_TIME", event.getPlayer()), Locale.ROOT);
            String parsed = format.format(new Date(time));
            LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_FINISHED", event.getPlayer(), parsed, FAILS.get(event.getPlayer()));
            updateJumpAndRunTime(event.getPlayer(), time);
            reset(event.getPlayer());
        }
    }

    private void updateJumpAndRunTime(Player player, long time) {
        long best = LEADERBOARD.getPlayerTime(SteamwarUser.get(player.getUniqueId()));
        if (time < best) {
            if (best != Long.MAX_VALUE) {
                SimpleDateFormat format = new SimpleDateFormat(LobbySystem.getMessage().parse("JUMP_AND_RUN_TIME", player), Locale.ROOT);
                String parsed = format.format(new Date(best - time));
                LobbySystem.getMessage().sendPrefixless("JUMP_AND_RUN_PERSONAL_BEST", player, parsed);
            }
            LEADERBOARD.updateBestTime(SteamwarUser.get(player.getUniqueId()), time);
        }
    }

    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        Player player = event.getPlayer();
        CURRENT_POS.remove(player);
        START.remove(player);
        FAILS.remove(player);
        CLICKED.remove(player);
        CLICKED_COUNT.remove(player);
    }

    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getItem() == null) return;
        if (event.getItem().getType() != Material.COMPASS) return;
        if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
        event.setCancelled(true);
        CLICKED.put(event.getPlayer(), System.currentTimeMillis());
    }

    public static void reset(Player player) {
        PlayerSpawn.giveItems(player);
        player.setAllowFlight(true);
        CURRENT_POS.remove(player);
        START.remove(player);
        FAILS.remove(player);
        CLICKED.remove(player);
        CLICKED_COUNT.remove(player);
    }
}
