package com.iridium.iridiumskyblock.listeners;

import com.iridium.iridiumskyblock.IridiumSkyblock;
import com.iridium.iridiumskyblock.Island;
import com.iridium.iridiumskyblock.IslandManager;
import com.iridium.iridiumskyblock.User;
import org.bukkit.Location;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.vehicle.VehicleDamageEvent;
import org.bukkit.projectiles.ProjectileSource;

import java.util.Objects;
import java.util.function.Supplier;

public class EntityDamageByEntityListener implements Listener {

    @EventHandler
    public void onEntityDamageEvent(EntityDamageEvent event) {
        final Entity damagee = event.getEntity();
        if (!(damagee instanceof Player)) return;
        final Player player = (Player) damagee;
        final User user = User.getUser(player);
        final Location damageeLocation = damagee.getLocation();
        final IslandManager islandManager = IridiumSkyblock.getIslandManager();
        final Island island = islandManager.getIslandViaLocation(damageeLocation);
        if (island == null) return;

        //The user is visiting this island, so disable damage
        if (user.islandID != island.getId() && IridiumSkyblock.getConfiguration().disablePvPOnIslands) {
            event.setCancelled(true);
        }

    }

    @EventHandler
    public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
        try {
            final Entity damagee = event.getEntity();
            final Location damageeLocation = damagee.getLocation();
            final IslandManager islandManager = IridiumSkyblock.getIslandManager();
            final Island island = islandManager.getIslandViaLocation(damageeLocation);
            if (island == null) return;

            final Entity damager = event.getDamager();

            // Using suppliers to defer work if unnecessary
            // This includes seemingly innocuous downcast operations
            final Supplier<Player> damageePlayerSupplier = () -> (Player) damagee;
            final Supplier<User> damageeUserSupplier = () -> User.getUser(damageePlayerSupplier.get());
            final Supplier<Island> damageeIslandSupplier = () -> damageeUserSupplier.get().getIsland();
            final Supplier<Arrow> arrowSupplier = () -> (Arrow) damager;
            final Supplier<ProjectileSource> projectileSourceSupplier = () -> arrowSupplier.get().getShooter();
            final Supplier<Player> shooterSupplier = () -> (Player) projectileSourceSupplier.get();
            final Supplier<User> shootingUserSupplier = () -> User.getUser(Objects.requireNonNull(shooterSupplier.get()));
            final Supplier<Player> damagingPlayerSupplier = () -> (Player) damager;
            final Supplier<User> damagingUserSupplier = () -> User.getUser(damagingPlayerSupplier.get());

            // Deals with two players pvping in IridiumSkyblock world
            if (IridiumSkyblock.getConfiguration().disablePvPOnIslands
                    && damagee instanceof Player
                    && damager instanceof Player) {
                event.setCancelled(true);
                return;
            }

            // Deals with A player getting damaged by a bow fired from a player in IridiumSkyblock world
            if (IridiumSkyblock.getConfiguration().disablePvPOnIslands
                    && damagee instanceof Player
                    && damager instanceof Arrow
                    && projectileSourceSupplier.get() instanceof Player) {
                event.setCancelled(true);
                return;
            }

            // Deals with a player attacking animals with bows that are not from their island
            if (damager instanceof Arrow
                    && !(damagee instanceof Player)
                    && projectileSourceSupplier.get() instanceof Player
                    && !island.getPermissions(shootingUserSupplier.get()).killMobs) {
                event.setCancelled(true);
                return;
            }

            // Deals with a player attacking animals that are not from their island
            if (damager instanceof Player
                    && !(damagee instanceof Player)
                    && !island.getPermissions(damagingUserSupplier.get()).killMobs) {
                event.setCancelled(true);
                return;
            }

            //Deals with a mob attacking a player that doesn't belong to the island (/is home traps?)
            if (IridiumSkyblock.getConfiguration().disablePvPOnIslands
                    && damagee instanceof Player
                    && !(damager instanceof Player)) {
                if (damageeIslandSupplier.get() != null) {
                    if (!damageeIslandSupplier.get().isInIsland(damager.getLocation())) {
                        event.setCancelled(true);
                        return;
                    }
                } else {
                    event.setCancelled(true);
                    return;
                }
            }

            // Deals with two allies pvping
            if (IridiumSkyblock.getConfiguration().disablePvPBetweenIslandMembers
                    && damagee instanceof Player
                    && damager instanceof Player
                    && damageeIslandSupplier.get() != null
                    && damageeIslandSupplier.get().equals(damagingUserSupplier.get().getIsland())) {
                event.setCancelled(true);
                return;
            }

            // Deals with two allies pvping with bows
            if (IridiumSkyblock.getConfiguration().disablePvPBetweenIslandMembers
                    && damagee instanceof Player
                    && damager instanceof Arrow
                    && projectileSourceSupplier.get() instanceof Player
                    && damageeIslandSupplier.get() != null
                    && damageeIslandSupplier.get().equals(damagingUserSupplier.get().getIsland())) {
                event.setCancelled(true);
                return;
            }
        } catch (Exception ex) {
            IridiumSkyblock.getInstance().sendErrorMessage(ex);
        }
    }

    @EventHandler
    public void onVehicleDamage(VehicleDamageEvent event) {
        try {
            final Vehicle vehicle = event.getVehicle();
            final Location location = vehicle.getLocation();
            final IslandManager islandManager = IridiumSkyblock.getIslandManager();
            final Island island = islandManager.getIslandViaLocation(location);
            if (island == null) return;

            final Entity attacker = event.getAttacker();
            if (!(attacker instanceof Player)) return;

            final Player attackerPlayer = (Player) attacker;
            final User attackerUser = User.getUser(attackerPlayer);

            if (!island.getPermissions(attackerUser).killMobs)
                event.setCancelled(true);
        } catch (Exception ex) {
            IridiumSkyblock.getInstance().sendErrorMessage(ex);
        }
    }
}