package com.peacecraftec.bukkit.lots.listener;

import com.peacecraftec.bukkit.internal.util.vault.VaultAPI;
import com.peacecraftec.bukkit.lots.LotPermissions;
import com.peacecraftec.bukkit.lots.PeacecraftLots;
import com.peacecraftec.bukkit.lots.core.Lot;
import com.peacecraftec.bukkit.lots.core.Town;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Hanging;
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.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.hanging.HangingBreakEvent;
import org.bukkit.event.hanging.HangingPlaceEvent;
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerBucketFillEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;

import java.util.Arrays;
import java.util.List;

import static com.peacecraftec.bukkit.internal.util.SenderUtil.getDisplayName;
import static com.peacecraftec.bukkit.internal.util.SenderUtil.sendMessage;

public class LotsListener implements Listener {
    private static final List<CreatureSpawnEvent.SpawnReason> ALLOWED_SPAWN_REASONS = Arrays.asList(CreatureSpawnEvent.SpawnReason.SPAWNER, CreatureSpawnEvent.SpawnReason.EGG, CreatureSpawnEvent.SpawnReason.SPAWNER_EGG, CreatureSpawnEvent.SpawnReason.LIGHTNING, CreatureSpawnEvent.SpawnReason.BUILD_SNOWMAN, CreatureSpawnEvent.SpawnReason.BUILD_IRONGOLEM, CreatureSpawnEvent.SpawnReason.BUILD_WITHER, CreatureSpawnEvent.SpawnReason.BREEDING, CreatureSpawnEvent.SpawnReason.SLIME_SPLIT, CreatureSpawnEvent.SpawnReason.DISPENSE_EGG, CreatureSpawnEvent.SpawnReason.CURED, CreatureSpawnEvent.SpawnReason.OCELOT_BABY, CreatureSpawnEvent.SpawnReason.CUSTOM);
    private static final List<Material> ALLOWED_INTERACT_BLOCKS = Arrays.asList(Material.WORKBENCH, Material.ENDER_CHEST, Material.ENCHANTMENT_TABLE);

    private PeacecraftLots module;

    public LotsListener(PeacecraftLots plugin) {
        this.module = plugin;
    }

    @EventHandler
    public void onBlockPlace(BlockPlaceEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockDamage(BlockDamageEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockIgnite(BlockIgniteEvent event) {
        if(event.getPlayer() != null) {
            Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
            Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
            if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
                sendMessage(event.getPlayer(), "lots.cannot-do");
                event.setCancelled(true);
            }
        } else {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockFromTo(BlockFromToEvent event) {
        Lot from = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Lot to = this.module.getLotManager().getLot(event.getToBlock().getLocation());
        Town fromTown = this.module.getLotManager().getTown(event.getBlock().getLocation());
        Town toTown = this.module.getLotManager().getTown(event.getToBlock().getLocation());
        if((from != null && to == null) || (from == null && to != null) || from != to || (fromTown != null && toTown == null) || (fromTown == null && toTown != null) || fromTown != toTown) {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockPistonExtend(BlockPistonExtendEvent event) {
        Lot from = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town fromTown = this.module.getLotManager().getTown(event.getBlock().getLocation());

        for(Block block : event.getBlocks()) {
            Block toBlock = block.getRelative(event.getDirection());

            Lot blockFrom = this.module.getLotManager().getLot(block.getLocation());
            Town blockFromTown = this.module.getLotManager().getTown(block.getLocation());
            Lot blockTo = this.module.getLotManager().getLot(toBlock.getLocation());
            Town blockToTown = this.module.getLotManager().getTown(toBlock.getLocation());
            if((from != null && blockTo == null) || (from == null && blockTo != null) || from != blockTo || (fromTown != null && blockToTown == null) || (fromTown == null && blockToTown != null) || fromTown != blockToTown || (blockFrom != null && blockTo == null) || (blockFrom == null && blockTo != null) || blockFrom != blockTo || (blockFromTown != null && blockToTown == null) || (blockFromTown == null && blockToTown != null) || blockFromTown != blockToTown) {
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onBlockPistonRetract(BlockPistonRetractEvent event) {
        Lot from = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town fromTown = this.module.getLotManager().getTown(event.getBlock().getLocation());

        for(Block block : event.getBlocks()) {
            Block toBlock = block.getRelative(event.getDirection());

            Lot blockFrom = this.module.getLotManager().getLot(block.getLocation());
            Town blockFromTown = this.module.getLotManager().getTown(block.getLocation());
            Lot blockTo = this.module.getLotManager().getLot(toBlock.getLocation());
            Town blockToTown = this.module.getLotManager().getTown(toBlock.getLocation());
            if((from != null && blockTo == null) || (from == null && blockTo != null) || from != blockTo || (fromTown != null && blockToTown == null) || (fromTown == null && blockToTown != null) || fromTown != blockToTown || (blockFrom != null && blockTo == null) || (blockFrom == null && blockTo != null) || blockFrom != blockTo || (blockFromTown != null && blockToTown == null) || (blockFromTown == null && blockToTown != null) || blockFromTown != blockToTown) {
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onBlockBurn(BlockBurnEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if(lot != null || town != null) {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockSpread(BlockSpreadEvent event) {
        Lot from = this.module.getLotManager().getLot(event.getSource().getLocation());
        Lot to = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town fromTown = this.module.getLotManager().getTown(event.getSource().getLocation());
        Town toTown = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if((event.getBlock().getType() == Material.FIRE && (from != null || to != null || fromTown != null || toTown != null)) || (from != null && to == null) || (from == null && to != null) || from != to || (fromTown != null && toTown == null) || (fromTown == null && toTown != null) || fromTown != toTown) {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onBlockExplode(BlockExplodeEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getBlock().getLocation());
        Town town = this.module.getLotManager().getTown(event.getBlock().getLocation());
        if(lot != null || town != null) {
            event.setYield(0);
            event.setCancelled(true);
            return;
        }

        for(Block block : event.blockList()) {
            Lot l = this.module.getLotManager().getLot(block.getLocation());
            Town t = this.module.getLotManager().getTown(block.getLocation());
            if(l != null || t != null) {
                event.setYield(0);
                event.setCancelled(true);
                break;
            }
        }
    }

    @EventHandler
    public void onCreatureSpawn(CreatureSpawnEvent event) {
        if(event.getEntity() != null && !ALLOWED_SPAWN_REASONS.contains(event.getSpawnReason()) && (!(event.getEntity() instanceof ArmorStand) || event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.DEFAULT)) {
            Lot lot = this.module.getLotManager().getLot(event.getEntity().getLocation());
            Town town = this.module.getLotManager().getTown(event.getEntity().getLocation());
            if(lot != null || town != null) {
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onEntityDamage(EntityDamageEvent event) {
        if(event.getEntity() != null) {
            Lot lot = this.module.getLotManager().getLot(event.getEntity().getLocation());
            Town town = this.module.getLotManager().getTown(event.getEntity().getLocation());
            if(event.getEntity() instanceof Player && (lot != null || town != null)) {
                event.setCancelled(true);
            } else if(lot != null && event instanceof EntityDamageByEntityEvent && (event.getEntity() instanceof Hanging || event.getEntity() instanceof ArmorStand)) {
                EntityDamageByEntityEvent entityEvent = (EntityDamageByEntityEvent) event;
                if(entityEvent.getDamager() instanceof Player && !entityEvent.getDamager().getName().equals(lot.getOwner()) && !entityEvent.getDamager().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
                    sendMessage(entityEvent.getDamager(), "lots.cannot-do");
                    event.setCancelled(true);
                }
            }
        }
    }

    @EventHandler
    public void onEntityExplode(EntityExplodeEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getEntity().getLocation());
        Town town = this.module.getLotManager().getTown(event.getEntity().getLocation());
        if(lot != null || town != null) {
            event.setYield(0);
            event.setCancelled(true);
            return;
        }

        for(Block block : event.blockList()) {
            Lot l = this.module.getLotManager().getLot(block.getLocation());
            Town t = this.module.getLotManager().getTown(block.getLocation());
            if(l != null || t != null) {
                event.setYield(0);
                event.setCancelled(true);
                break;
            }
        }
    }

    @EventHandler
    public void onEntityTarget(EntityTargetEvent event) {
        if(event.getTarget() instanceof Player) {
            Lot lot = this.module.getLotManager().getLot(event.getTarget().getLocation());
            Town town = this.module.getLotManager().getTown(event.getTarget().getLocation());
            if(lot != null || town != null) {
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onHangingPlace(HangingPlaceEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getEntity().getLocation());
        Town town = this.module.getLotManager().getTown(event.getEntity().getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onHangingBreak(HangingBreakEvent event) {
        if(event instanceof HangingBreakByEntityEvent) {
            Entity attacker = ((HangingBreakByEntityEvent) event).getRemover();
            if(attacker instanceof Player) {
                Player player = (Player) attacker;
                Lot lot = this.module.getLotManager().getLot(event.getEntity().getLocation());
                Town town = this.module.getLotManager().getTown(event.getEntity().getLocation());
                if(((lot != null && !lot.canBuild(player)) || (lot == null && town != null)) && !player.hasPermission(LotPermissions.BUILD_ANYWHERE)) {
                    event.setCancelled(true);
                }
            }
        }
    }

    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.PHYSICAL) {
            Block block = event.getClickedBlock();
            Lot lot = this.module.getLotManager().getLot(block.getLocation());
            if(block.getState() instanceof Sign && ((Sign) block.getState()).getLine(0).equalsIgnoreCase("[BuyLot]") && lot != null && lot.isForSale()) {
                Town town = lot.getTown();
                if(!event.getPlayer().hasPermission(town.getPermission())) {
                    sendMessage(event.getPlayer(), "lots.town-no-perms", lot.getId());
                    return;
                }

                int count = 0;
                for(Lot l : this.module.getLotManager().getPlayerLots(event.getPlayer().getName())) {
                    if(l.getTown().getName().equals(lot.getTown().getName())) {
                        count++;
                    }
                }

                int max = this.module.getConfig().getInteger("max-town-lots-per-player", 1);
                if(count > max && !event.getPlayer().hasPermission(LotPermissions.RESTRICTION_OVERRIDE)) {
                    sendMessage(event.getPlayer(), "lot.lot-cant-have-more", max);
                    return;
                }

                double money = VaultAPI.getBalance(event.getPlayer().getName(), event.getPlayer().getWorld().getName());
                double price = lot.getPrice();
                if(money < price) {
                    sendMessage(event.getPlayer(), "lots.lot-not-enough-money");
                    return;
                }

                VaultAPI.withdrawPlayer(event.getPlayer().getName(), event.getPlayer().getWorld().getName(), price);
                if(!lot.getOwner().equals("")) {
                    VaultAPI.depositPlayer(lot.getOwner(), event.getPlayer().getWorld().getName(), price);
                }

                lot.setForSale(false);
                lot.setPrice(0);
                lot.setOwner(event.getPlayer().getName());
                this.module.getLotManager().saveLot(lot);
                sendMessage(event.getPlayer(), "lots.lot-purchased", lot.getId(), VaultAPI.format(price) + " " + (lot.getPrice() <= 1 ? VaultAPI.currencyNameSingular() : VaultAPI.currencyNamePlural()));
                event.setCancelled(true);
                return;
            }

            Town town = this.module.getLotManager().getTown(block.getLocation());
            if(!ALLOWED_INTERACT_BLOCKS.contains(block.getType()) && ((lot != null && !lot.canInteract(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.DO_ANYTHING)) {
                sendMessage(event.getPlayer(), "lots.cannot-do");
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
        Lot lot = this.module.getLotManager().getLot(event.getRightClicked().getLocation());
        Town town = this.module.getLotManager().getTown(event.getRightClicked().getLocation());
        if(((lot != null && !lot.canInteract(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.DO_ANYTHING)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onPlayerArmorStandManipulate(PlayerArmorStandManipulateEvent event) {
        this.onPlayerInteractEntity(event);
    }

    @EventHandler
    public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
        this.onPlayerInteractEntity(event);
    }

    @EventHandler
    public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
        Block block = event.getBlockClicked().getRelative(event.getBlockFace());
        Lot lot = this.module.getLotManager().getLot(block.getLocation());
        Town town = this.module.getLotManager().getTown(block.getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onPlayerBucketFill(PlayerBucketFillEvent event) {
        Block block = event.getBlockClicked();
        Lot lot = this.module.getLotManager().getLot(block.getLocation());
        Town town = this.module.getLotManager().getTown(block.getLocation());
        if(((lot != null && !lot.canBuild(event.getPlayer())) || (lot == null && town != null)) && !event.getPlayer().hasPermission(LotPermissions.BUILD_ANYWHERE)) {
            sendMessage(event.getPlayer(), "lots.cannot-do");
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onPlayerMove(PlayerMoveEvent event) {
        Lot from = this.module.getLotManager().getLot(event.getFrom());
        Lot to = this.module.getLotManager().getLot(event.getTo());
        if(from == null && to != null) {
            sendMessage(event.getPlayer(), "lots.lot-entered-" + (!to.getOwner().equals("") ? "owned" : "unowned"), to.getId(), getDisplayName(to.getOwner(), to.getWorld().getName()));
        } else if(from != null && to == null) {
            sendMessage(event.getPlayer(), "lots.lot-left", from.getId());
        }

        Town frmTown = this.module.getLotManager().getTown(event.getFrom());
        Town toTown = this.module.getLotManager().getTown(event.getTo());
        if(frmTown == null && toTown != null) {
            sendMessage(event.getPlayer(), "lots.town-entered", toTown.getName());
        } else if(frmTown != null && toTown == null) {
            sendMessage(event.getPlayer(), "lots.town-left", frmTown.getName());
        }
    }

}