package de.teleportsuite.managers;

import de.teleportsuite.TeleportSuite;
import de.teleportsuite.models.TeleportLocation;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;

import java.util.*;

public class TeleportManager {
    private final TeleportSuite plugin;
    // Pending TPA requests: requester -> target
    private final Map<UUID, UUID> tpaRequests = new HashMap<>();
    private final Map<UUID, Long> requestTimestamps = new HashMap<>();
    // Pending cross-server TPA requests: requesterName -> targetName
    private final Map<String, String> crossServerTpaRequests = new HashMap<>();
    private final Map<String, Long> crossServerRequestTimestamps = new HashMap<>();
    // Cooldowns
    private final Map<UUID, Long> cooldowns = new HashMap<>();
    // Warmup tasks
    private final Map<UUID, Integer> warmupTasks = new HashMap<>();
    // Saved "before teleport" locations (for /back)
    private final Map<UUID, TeleportLocation> pendingBackLocations = new HashMap<>();

    public TeleportManager(TeleportSuite plugin) { this.plugin = plugin; }

    public void teleport(Player player, TeleportLocation destination) {
        teleport(player, destination, true);
    }

    public void teleport(Player player, TeleportLocation destination, boolean saveBack) {
        int delay = plugin.getConfigManager().getTeleportDelay();
        if (player.hasPermission("teleportsuite.nodelay")) delay = 0;

        // Check cooldown
        if (!player.hasPermission("teleportsuite.nocooldown")) {
            long cooldownMs = plugin.getConfigManager().getTeleportCooldown() * 1000L;
            long lastTp = cooldowns.getOrDefault(player.getUniqueId(), 0L);
            if (System.currentTimeMillis() - lastTp < cooldownMs) {
                long remaining = (cooldownMs - (System.currentTimeMillis() - lastTp)) / 1000;
                player.sendMessage(plugin.getConfigManager().getMessage("teleport-cooldown", "seconds", String.valueOf(remaining)));
                return;
            }
        }

        if (saveBack) pendingBackLocations.put(player.getUniqueId(), new TeleportLocation(player.getLocation(), plugin.getConfigManager().getServerName()));

        if (delay <= 0) {
            executeTeleport(player, destination);
            return;
        }

        player.sendMessage(plugin.getConfigManager().getMessage("teleport-warmup", "seconds", String.valueOf(delay)));
        Location startLoc = player.getLocation().clone();

        int taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
            if (plugin.getConfigManager().cancelOnMove()) {
                Location now = player.getLocation();
                if (Math.abs(now.getX()-startLoc.getX()) > 0.5 || Math.abs(now.getZ()-startLoc.getZ()) > 0.5) {
                    player.sendMessage(plugin.getConfigManager().getMessage("teleport-cancelled"));
                    warmupTasks.remove(player.getUniqueId());
                    return;
                }
            }
            executeTeleport(player, destination);
            warmupTasks.remove(player.getUniqueId());
        }, delay * 20L);

        warmupTasks.put(player.getUniqueId(), taskId);
    }

    private void executeTeleport(Player player, TeleportLocation dest) {
        String localServer = plugin.getConfigManager().getServerName();

        if (!dest.isLocalServer(localServer)) {
            // BungeeCord-Teleport
            if (plugin.getBungeeMessenger() != null) {
                plugin.getBungeeMessenger().connectToServer(player, dest.getServer(), dest.getWorld(),
                        dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch());
                player.sendMessage(plugin.getConfigManager().getMessage("teleport-success"));
            }
            return;
        }

        Location loc = dest.toBukkitLocation();
        if (loc == null || loc.getWorld() == null) {
            player.sendMessage("§cZielwelt nicht gefunden!");
            return;
        }

        // Save last location to database
        plugin.getDatabaseManager().saveLastLocation(player.getUniqueId(),
                new TeleportLocation(player.getLocation(), localServer));

        player.teleport(loc);
        cooldowns.put(player.getUniqueId(), System.currentTimeMillis());
        player.sendMessage(plugin.getConfigManager().getMessage("teleport-success"));
    }

    public void cancelWarmup(UUID uuid) {
        Integer taskId = warmupTasks.remove(uuid);
        if (taskId != null) Bukkit.getScheduler().cancelTask(taskId);
    }

    public TeleportLocation getBackLocation(UUID uuid) {
        return pendingBackLocations.get(uuid);
    }

    // TPA
    public void sendTpaRequest(Player from, Player to) {
        tpaRequests.put(from.getUniqueId(), to.getUniqueId());
        requestTimestamps.put(from.getUniqueId(), System.currentTimeMillis());

        to.sendMessage(plugin.getConfigManager().getMessage("tpa-received", "player", from.getName()));
        from.sendMessage(plugin.getConfigManager().getMessage("tpa-sent", "player", to.getName()));

        int timeout = plugin.getConfigManager().getRequestTimeout();
        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
            if (tpaRequests.containsKey(from.getUniqueId())) {
                tpaRequests.remove(from.getUniqueId());
                requestTimestamps.remove(from.getUniqueId());
                if (from.isOnline()) from.sendMessage(plugin.getConfigManager().getMessage("tpa-expired"));
            }
        }, timeout * 20L);
    }

    public void sendTpaRequest(Player from, String targetName) {
        if (plugin.getBungeeMessenger() == null) {
            from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName));
            return;
        }

        plugin.getBungeeMessenger().requestPlayerServer(from, targetName, server -> {
            if (server == null || server.isBlank()) {
                from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName));
                return;
            }

            String localServer = plugin.getConfigManager().getServerName();
            if (server.equalsIgnoreCase(localServer)) {
                Player localTarget = Bukkit.getPlayerExact(targetName);
                if (localTarget == null || !localTarget.isOnline()) {
                    from.sendMessage(plugin.getConfigManager().getMessage("player-not-found", "player", targetName));
                    return;
                }
                sendTpaRequest(from, localTarget);
                return;
            }

            String requesterKey = from.getName();
            crossServerTpaRequests.put(requesterKey, targetName);
            crossServerRequestTimestamps.put(requesterKey, System.currentTimeMillis());

            plugin.getBungeeMessenger().sendTpaRequestToPlayer(from, targetName);
            from.sendMessage(plugin.getConfigManager().getMessage("tpa-sent", "player", targetName));

            int timeout = plugin.getConfigManager().getRequestTimeout();
            Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
                if (!crossServerTpaRequests.containsKey(requesterKey)) return;
                crossServerTpaRequests.remove(requesterKey);
                crossServerRequestTimestamps.remove(requesterKey);

                Player requester = Bukkit.getPlayerExact(from.getName());
                if (requester != null && requester.isOnline()) {
                    requester.sendMessage(plugin.getConfigManager().getMessage("tpa-expired"));
                }
                if (plugin.getBungeeMessenger() != null) {
                    plugin.getBungeeMessenger().sendTpaExpiredToPlayer(from, targetName);
                }
            }, timeout * 20L);
        });
    }

    public void receiveCrossServerTpaRequest(String requesterName, String targetName) {
        Player target = Bukkit.getPlayerExact(targetName);
        if (target == null || !target.isOnline()) return;

        crossServerTpaRequests.put(requesterName, targetName);
        crossServerRequestTimestamps.put(requesterName, System.currentTimeMillis());
        target.sendMessage(plugin.getConfigManager().getMessage("tpa-received", "player", requesterName));
    }

    public void receiveCrossServerTpaAccepted(String requesterName, String targetName) {
        Player requester = Bukkit.getPlayerExact(requesterName);
        if (requester == null || !requester.isOnline()) return;

        String requesterKey = findRequesterKey(requesterName);
        if (requesterKey != null) {
            crossServerTpaRequests.remove(requesterKey);
            crossServerRequestTimestamps.remove(requesterKey);
        }
        requester.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", targetName));

        if (plugin.getBungeeMessenger() != null) {
            plugin.getBungeeMessenger().teleportToPlayer(requester, targetName);
        }
    }

    public void receiveCrossServerTpaDenied(String requesterName, String targetName) {
        Player requester = Bukkit.getPlayerExact(requesterName);
        if (requester == null || !requester.isOnline()) return;

        String requesterKey = findRequesterKey(requesterName);
        if (requesterKey != null) {
            crossServerTpaRequests.remove(requesterKey);
            crossServerRequestTimestamps.remove(requesterKey);
        }
        requester.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", targetName));
    }

    public void receiveCrossServerTpaExpired(String requesterName, String targetName) {
        Player target = Bukkit.getPlayerExact(targetName);
        if (target == null || !target.isOnline()) return;

        String requesterKey = findRequesterKey(requesterName);
        if (requesterKey != null && target.getName().equalsIgnoreCase(crossServerTpaRequests.get(requesterKey))) {
            crossServerTpaRequests.remove(requesterKey);
            crossServerRequestTimestamps.remove(requesterKey);
        }
    }

    public boolean acceptTpa(Player target) {
        UUID requester = null;
        for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) {
            if (entry.getValue().equals(target.getUniqueId())) {
                requester = entry.getKey();
                break;
            }
        }
        if (requester != null) {
            Player from = Bukkit.getPlayer(requester);
            tpaRequests.remove(requester);
            requestTimestamps.remove(requester);

            if (from != null && from.isOnline()) {
                from.sendMessage(plugin.getConfigManager().getMessage("tpa-accepted", "player", target.getName()));
                teleport(from, new TeleportLocation(target.getLocation(), plugin.getConfigManager().getServerName()));
            }
            return true;
        }

        String requesterName = findRequesterByTarget(target.getName());

        if (requesterName == null) return false;

        crossServerTpaRequests.remove(requesterName);
        crossServerRequestTimestamps.remove(requesterName);

        if (plugin.getBungeeMessenger() != null) {
            plugin.getBungeeMessenger().sendTpaAcceptToPlayer(target, requesterName);
        }
        return true;
    }

    public boolean denyTpa(Player target) {
        UUID requester = null;
        for (Map.Entry<UUID, UUID> entry : tpaRequests.entrySet()) {
            if (entry.getValue().equals(target.getUniqueId())) {
                requester = entry.getKey();
                break;
            }
        }
        if (requester != null) {
            Player from = Bukkit.getPlayer(requester);
            tpaRequests.remove(requester);
            requestTimestamps.remove(requester);

            if (from != null && from.isOnline()) {
                from.sendMessage(plugin.getConfigManager().getMessage("tpa-denied", "player", target.getName()));
            }
            return true;
        }

        String requesterName = findRequesterByTarget(target.getName());

        if (requesterName == null) return false;

        crossServerTpaRequests.remove(requesterName);
        crossServerRequestTimestamps.remove(requesterName);

        if (plugin.getBungeeMessenger() != null) {
            plugin.getBungeeMessenger().sendTpaDenyToPlayer(target, requesterName);
        }
        return true;
    }

    private String findRequesterByTarget(String targetName) {
        for (Map.Entry<String, String> entry : crossServerTpaRequests.entrySet()) {
            if (targetName.equalsIgnoreCase(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

    private String findRequesterKey(String requesterName) {
        for (String key : crossServerTpaRequests.keySet()) {
            if (key.equalsIgnoreCase(requesterName)) {
                return key;
            }
        }
        return null;
    }
}
