package tc.oc.pgm.listeners;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.kyori.text.TranslatableComponent;
import net.kyori.text.format.TextColor;
import org.bukkit.*;
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.inventory.InventoryType;
import org.bukkit.event.player.PlayerAttackEntityEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import tc.oc.pgm.api.Permissions;
import tc.oc.pgm.api.match.MatchManager;
import tc.oc.pgm.api.player.MatchPlayer;
import tc.oc.pgm.api.player.ParticipantState;
import tc.oc.pgm.spawns.events.ObserverKitApplyEvent;
import tc.oc.pgm.tnt.TNTMatchModule;
import tc.oc.pgm.tracker.Trackers;
import tc.oc.pgm.util.named.NameStyle;
import tc.oc.pgm.util.text.TextFormatter;
import tc.oc.pgm.util.text.TextTranslations;

public class AntiGriefListener implements Listener {

  private static final Material DEFUSE_ITEM = Material.SHEARS;
  private static final int DEFUSE_SLOT = 4;

  private final MatchManager mm;

  public AntiGriefListener(MatchManager mm) {
    this.mm = mm;
  }

  @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) {
    // check tnt
    if (!(entity instanceof TNTPrimed)) return;

    TNTMatchModule tntmm = mm.getMatch(player.getWorld()).getModule(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
              + TextTranslations.translate("moderation.defuse.water", clicker.getBukkit()));
      return;
    }

    // check owner
    MatchPlayer owner = this.mm.getPlayer(Trackers.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
                + TextTranslations.translate(
                    "moderation.defuse.player",
                    clicker.getBukkit(),
                    owner.getBukkit().getDisplayName(clicker.getBukkit()) + ChatColor.RED));
      } else {
        this.notifyDefuse(
            clicker,
            entity,
            ChatColor.RED
                + TextTranslations.translate("moderation.defuse.world", clicker.getBukkit()));
      }
    }
  }

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

  @EventHandler(priority = EventPriority.HIGHEST)
  public void checkDefuse(final PlayerInteractEvent event) {
    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(Permissions.DEFUSE)) {
      if (event.getAction() == Action.RIGHT_CLICK_AIR) {
        this.obsTntDefuse(clicker, event.getPlayer().getLocation());
      } else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
        this.obsTntDefuse(clicker, event.getClickedBlock().getLocation());
      }
    }
  }

  private void obsTntDefuse(MatchPlayer player, Location loc) {
    List<ParticipantState> owners = this.removeTnt(loc, 5.0);
    if (owners != null && !owners.isEmpty()) {
      player.sendMessage(
          TranslatableComponent.of(
              "moderation.defuse.player",
              TextFormatter.nameList(owners, NameStyle.COLOR, TextColor.WHITE)));
    }
  }

  // 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 = Trackers.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) {
    if (event.getPlayer().getParty() == null) return;
    if (!event.getPlayer().getParty().isObserving()
        || !event.getPlayer().getBukkit().hasPermission(Permissions.DEFUSE)) return;

    ItemStack shears = new ItemStack(DEFUSE_ITEM);

    // TODO: Update information if locale changes
    ItemMeta meta = shears.getItemMeta();
    meta.setDisplayName(
        ChatColor.RED
            + ChatColor.BOLD.toString()
            + TextTranslations.translate(
                "moderation.defuse.displayName", event.getPlayer().getBukkit()));
    meta.setLore(
        Collections.singletonList(
            ChatColor.GRAY
                + TextTranslations.translate(
                    "moderation.defuse.tooltip", event.getPlayer().getBukkit())));
    shears.setItemMeta(meta);

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

  @EventHandler(priority = EventPriority.MONITOR)
  public void cloneCraftingWindow(final PlayerInteractEvent event) {
    if (!event.isCancelled()
        && event.getAction() == Action.RIGHT_CLICK_BLOCK
        && event.getPlayer().getOpenInventory().getType()
            == InventoryType.CRAFTING /* nothing open */) {
      Block block = event.getClickedBlock();
      if (block != null
          && block.getType() == Material.WORKBENCH
          && !event.getPlayer().isSneaking()) {
        // create the window ourself
        event.setCancelled(true);
        event.getPlayer().openWorkbench(null, true); // doesn't check reachable
      }
    }
  }
}