package tc.oc.pgm.antigrief;

import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;

import com.google.common.collect.Lists;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
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.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import tc.oc.commons.bukkit.channels.AdminChannel;
import tc.oc.commons.bukkit.chat.ComponentRenderers;
import tc.oc.commons.bukkit.chat.ListComponent;
import tc.oc.commons.bukkit.chat.NameStyle;
import tc.oc.commons.bukkit.event.ObserverKitApplyEvent;
import tc.oc.commons.core.inject.Proxied;
import tc.oc.commons.core.plugin.PluginFacet;
import tc.oc.pgm.PGMTranslations;
import tc.oc.pgm.match.MatchManager;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.match.ParticipantState;
import tc.oc.pgm.tnt.TNTMatchModule;
import tc.oc.pgm.tracker.EntityResolver;

public class DefuseListener implements PluginFacet, Listener {
    public static final Material DEFUSE_ITEM = Material.SHEARS;
    public static final int DEFUSE_SLOT = 4;

    private final MatchManager mm;
    private final EntityResolver entityResolver;
    private final AdminChannel adminChannel;

    @Inject DefuseListener(MatchManager mm, @Proxied EntityResolver entityResolver, AdminChannel adminChannel) {
        this.mm = mm;
        this.entityResolver = entityResolver;
        this.adminChannel = adminChannel;
    }

    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
    public void rightClickDefuse(final PlayerInteractEntityEvent event) {
        ItemStack hand = event.getPlayer().getItemInHand();
        if(hand == null || hand.getType() != DEFUSE_ITEM) return;

        this.participantDefuse(event.getPlayer(), event.getRightClicked());
    }

    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
    public void leftClickDefuse(final PlayerAttackEntityEvent event) {
        this.participantDefuse(event.getPlayer(), event.getLeftClicked());
    }

    private void participantDefuse(Player player, Entity entity) {
        if(!AntiGrief.Defuse.enabled()) return;

        // check tnt
        if(!(entity instanceof TNTPrimed)) return;

        TNTMatchModule tntmm = mm.getMatch(player.getWorld()).getMatchModule(TNTMatchModule.class);
        if(tntmm != null && !tntmm.getProperties().friendlyDefuse) return;

        MatchPlayer clicker = this.mm.getPlayer(player);
        if(clicker == null || !clicker.canInteract()) return;

        // check water
        Block block = entity.getLocation().getBlock();
        if(block != null && (block.getType() == Material.WATER || block.getType() == Material.STATIONARY_WATER)) {
            clicker.sendMessage(ChatColor.RED + PGMTranslations.t("defuse.water", clicker));
            return;
        }

        // check owner
        MatchPlayer owner = this.mm.getPlayer(entityResolver.getOwner(entity));
        if(owner == null || (owner != clicker && owner.getParty() == clicker.getParty())) { // cannot defuse own TNT
            // defuse TNT
            entity.remove();
            if(owner != null) {
                this.notifyDefuse(clicker, entity, ChatColor.RED + PGMTranslations.t("defuse.player", clicker, owner.getDisplayName(clicker) + ChatColor.RED));
                adminChannel.broadcast(clicker.getDisplayName() +
                                       ChatColor.WHITE + " defused " +
                                       owner.getDisplayName()
                                       + ChatColor.WHITE + "'s " +
                                       ChatColor.DARK_RED + "TNT");
            } else {
                this.notifyDefuse(clicker, entity, ChatColor.RED + PGMTranslations.t("defuse.world", clicker));
            }
        }
    }

    private void notifyDefuse(MatchPlayer clicker, Entity entity, String message) {
        clicker.sendMessage(message);
        for(Player viewer : Bukkit.getOnlinePlayers()) {
            viewer.playSound(entity.getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 1, 1);
        }
    }

    @EventHandler(priority = EventPriority.HIGHEST)
    public void checkDefuse(final PlayerInteractEvent event) {
        if(!AntiGrief.Defuse.enabled()) return;
        ItemStack hand = event.getPlayer().getItemInHand();
        if(hand == null || hand.getType() != DEFUSE_ITEM) return;

        MatchPlayer clicker = this.mm.getPlayer(event.getPlayer());
        if(clicker != null && clicker.isObserving() && clicker.getBukkit().hasPermission("pgm.defuse")) {
            if(event.getAction() == Action.RIGHT_CLICK_AIR) {
                this.obsTntDefuse(clicker.getBukkit(), event.getPlayer().getLocation());
            } else if(event.getAction() == Action.RIGHT_CLICK_BLOCK) {
                this.obsTntDefuse(clicker.getBukkit(), event.getClickedBlock().getLocation());
            }
        }
    }

    private void obsTntDefuse(Player player, Location loc) {
        List<ParticipantState> owners = this.removeTnt(loc, 5.0);
        if(owners != null && !owners.isEmpty()) {
            ComponentRenderers.send(
                player,
                new TranslatableComponent(
                    "defuse.player",
                    new ListComponent(
                        Lists.transform(owners, owner -> owner.getStyledName(NameStyle.COLOR))
                    )
                )
            );
        }
    }

    // Original code borrowed from WorldEdit
    private List<ParticipantState> removeTnt(Location origin, double radius) {
        if(radius <= 0) return null;

        List<ParticipantState> owners = new ArrayList<>();
        double radiusSq = radius * radius;
        for(Entity ent : origin.getWorld().getEntities()) {
            if(origin.distanceSquared(ent.getLocation()) > radiusSq) continue;

            if(ent instanceof TNTPrimed) {
                ParticipantState player = entityResolver.getOwner(ent);

                if(player != null) {
                    owners.add(player);
                }
                ent.remove();

            }
        }

        List<ParticipantState> uniqueOwners = new ArrayList<>();
        for(ParticipantState player : owners) {
            if(!uniqueOwners.contains(player)) {
                uniqueOwners.add(player);
            }
        }
        return uniqueOwners;
    }

    @EventHandler
    public void giveKit(final ObserverKitApplyEvent event) {
        final MatchPlayer player = mm.getPlayer(event.getPlayer());
        if(player == null) return;
        if(!player.isObservingType()) return;
        if(!player.getBukkit().hasPermission("pgm.defuse")) return;

        ItemStack shears = new ItemStack(DEFUSE_ITEM);

        // TODO: Update information if locale changes
        ItemMeta meta = shears.getItemMeta();
        meta.addItemFlags(ItemFlag.values());
        meta.setDisplayName(PGMTranslations.t("defuse.displayName", player));
        meta.setLore(Lists.newArrayList(ChatColor.GRAY + PGMTranslations.t("defuse.tooltip", player)));
        shears.setItemMeta(meta);

        event.getPlayer().getInventory().setItem(DEFUSE_SLOT, shears);
    }
}
