package cat.nyaa.nyaautils.sit;

import cat.nyaa.nyaautils.I18n;
import cat.nyaa.nyaautils.NyaaUtils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import org.spigotmc.event.entity.EntityDismountEvent;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class SitListener implements Listener {
    public static String metadata_key = "nyaautils_chair";
    public final NyaaUtils plugin;
    public HashMap<UUID, Location> safeLocations = new HashMap<>();
    public Set<UUID> enabledPlayers = new HashSet<>();
    public LoadingCache<UUID, Boolean> messageCooldown = CacheBuilder.newBuilder()
                                                                     .expireAfterWrite(2, TimeUnit.SECONDS)
                                                                     .build(
                                                                             new CacheLoader<UUID, Boolean>() {
                                                                                 @Override
                                                                                 public Boolean load(UUID key) throws Exception {
                                                                                     return true;
                                                                                 }
                                                                             }
                                                                     );

    public SitListener(NyaaUtils plugin) {
        this.plugin = plugin;
        plugin.getServer().getPluginManager().registerEvents(this, plugin);
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void onClickBlock(PlayerInteractEvent event) {
        if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.hasBlock() && !event.hasItem()) {
            Block block = event.getClickedBlock();
            BlockFace face = event.getBlockFace();
            if (face == BlockFace.DOWN || block.isLiquid() || !plugin.cfg.sit_blocks.contains(block.getType())) {
                return;
            }
            Block relative = block.getRelative(0, 1, 0);
            Player player = event.getPlayer();
            if (messageCooldown.getIfPresent(player.getUniqueId()) != null) {
                return;
            }
            messageCooldown.put(player.getUniqueId(), true);
            if (!player.hasPermission("nu.sit") || !enabledPlayers.contains(player.getUniqueId()) || player.isInsideVehicle() || !player.getPassengers().isEmpty() || player.getGameMode() == GameMode.SPECTATOR || !player.isOnGround()) {
                return;
            }
            if (relative.isLiquid() || !(relative.isEmpty() || relative.isPassable())) {
                player.sendMessage(I18n.format("user.sit.invalid_location"));
                return;
            }
            Vector vector = block.getBoundingBox().getCenter().clone();
            Location loc = vector.setY(block.getBoundingBox().getMaxY()).toLocation(player.getWorld()).clone();
            for (SitLocation sl : plugin.cfg.sit_locations.values()) {
                if (sl.blocks != null && sl.x != null && sl.y != null && sl.z != null && sl.blocks.contains(block.getType().name())) {
                    loc.add(sl.x, sl.y, sl.z);
                }
            }
            if (block.getBlockData() instanceof Directional) {
                face = ((Directional) block.getBlockData()).getFacing();
                if (face == BlockFace.EAST) {
                    loc.setYaw(90);
                } else if (face == BlockFace.WEST) {
                    loc.setYaw(-90);
                } else if (face == BlockFace.NORTH) {
                    loc.setYaw(0);
                } else if (face == BlockFace.SOUTH) {
                    loc.setYaw(-180);
                }
            } else {
                if (face == BlockFace.WEST) {
                    loc.setYaw(90);
                } else if (face == BlockFace.EAST) {
                    loc.setYaw(-90);
                } else if (face == BlockFace.SOUTH) {
                    loc.setYaw(0);
                } else if (face == BlockFace.NORTH) {
                    loc.setYaw(-180);
                } else {
                    loc.setYaw(player.getEyeLocation().getYaw());
                }
            }
            for (Entity e : loc.getWorld().getNearbyEntities(loc, 0.5, 0.7, 0.5)) {
                if (e instanceof LivingEntity) {
                    if (e.hasMetadata(metadata_key) || (e instanceof Player && e.isInsideVehicle() && e.getVehicle().hasMetadata(metadata_key))) {
                        player.sendMessage(I18n.format("user.sit.invalid_location"));
                        return;
                    }
                }
            }
            Location safeLoc = player.getLocation().clone();
            ArmorStand armorStand = loc.getWorld().spawn(loc, ArmorStand.class, (e) -> {
                e.setVisible(false);
                e.setPersistent(false);
                e.setCanPickupItems(false);
                e.setBasePlate(false);
                e.setArms(false);
                e.setMarker(true);
                e.setInvulnerable(true);
                e.setGravity(false);
            });
            if (armorStand != null) {
                armorStand.setMetadata(metadata_key, new FixedMetadataValue(plugin, true));
                if (armorStand.addPassenger(player)) {
                    safeLocations.put(player.getUniqueId(), safeLoc);
                } else {
                    armorStand.remove();
                }
            }
        }
    }

    @EventHandler
    public void onDismount(EntityDismountEvent event) {
        if (event.getDismounted() instanceof ArmorStand) {
            ArmorStand armorStand = (ArmorStand) event.getDismounted();
            if (armorStand.hasMetadata(metadata_key)) {
                for (Entity p : armorStand.getPassengers()) {
                    if (p.isValid()) {
                        Location loc = safeLocations.get(p.getUniqueId());
                        safeLocations.remove(p.getUniqueId());
                        new BukkitRunnable() {
                            @Override
                            public void run() {
                                if (p.isValid() && loc != null) {
                                    p.teleport(loc);
                                }
                            }
                        }.runTask(plugin);
                    }
                }
            }
            armorStand.remove();
        }
    }

    @EventHandler
    public void onTeleport(PlayerTeleportEvent event) {
        safeLocations.remove(event.getPlayer().getUniqueId());
    }

    @EventHandler
    public void onPlayerDeath(PlayerDeathEvent event) {
        safeLocations.remove(event.getEntity().getUniqueId());
    }

    @EventHandler
    public void onQuit(PlayerQuitEvent event) {
        Player player = event.getPlayer();
        Entity vehicle = player.getVehicle();
        if (safeLocations.containsKey(player.getUniqueId()) && player.isInsideVehicle() && vehicle instanceof ArmorStand) {
            safeLocations.remove(player.getUniqueId());
            player.leaveVehicle();
            vehicle.remove();
        }
    }
}