package fr.luc.gamingcore.listener;

import fr.luc.gamingcore.manager.GuildManager;
import fr.luc.gamingcore.model.Guild;
import fr.luc.gamingcore.model.GuildPermission;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;

import java.util.Objects;
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.block.BlockBreakEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;

/**
 * Écouteur d'événements garantissant la protection territoriale des chunks de guilde contre le grief et les intrusions.
 */
public class TerritoryProtectionListener implements Listener {

    private final GuildManager guildManager;

    /**
     * Constructeur de l'écouteur de protection territoriale.
     *
     * @param guildManager Le gestionnaire de guildes.
     */
    public TerritoryProtectionListener(GuildManager guildManager) {
        this.guildManager = guildManager;
    }

    /**
     * Intercepte la destruction de blocs et vérifie si le joueur possède la permission de casser dans le claim.
     *
     * @param event L'événement BlockBreakEvent.
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    public void onBlockBreak(BlockBreakEvent event) {
        Player player = event.getPlayer();
        if (player.hasPermission("guild.bypass")) {
            return;
        }

        Block block = event.getBlock();
        Guild guildAtBlock = guildManager.getGuildAt(block.getWorld().getName(), block.getX() >> 4, block.getZ() >> 4);
        if (guildAtBlock == null) {
            return; // Territoire libre
        }

        Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
        if (playerGuild == null || !playerGuild.getId().equals(guildAtBlock.getId())) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
            return;
        }

        // Le joueur est dans sa propre guilde : vérifier la permission BREAK
        if (!guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.BREAK)) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>Votre rang de guilde ne vous autorise pas à casser des blocs.</red>");
        }
    }

    /**
     * Intercepte la pose de blocs dans un territoire revendiqué.
     *
     * @param event L'événement BlockPlaceEvent.
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    public void onBlockPlace(BlockPlaceEvent event) {
        Player player = event.getPlayer();
        if (player.hasPermission("guild.bypass")) {
            return;
        }

        Block block = event.getBlock();
        Guild guildAtBlock = guildManager.getGuildAt(block.getWorld().getName(), block.getX() >> 4, block.getZ() >> 4);
        if (guildAtBlock == null) {
            return;
        }

        Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
        if (playerGuild == null || !playerGuild.getId().equals(guildAtBlock.getId())) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
            return;
        }

        // Vérifier la permission BUILD
        if (!guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.BUILD)) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>Votre rang de guilde ne vous autorise pas à poser des blocs.</red>");
        }
    }

    /**
     * Intercepte les interactions avec les conteneurs (coffres, fours) et mécanismes (portes, boutons, leviers).
     *
     * @param event L'événement PlayerInteractEvent.
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock() == null) {
            return;
        }

        Player player = event.getPlayer();
        if (player.hasPermission("guild.bypass")) {
            return;
        }

        Block block = event.getClickedBlock();
        Guild guildAtBlock = guildManager.getGuildAt(block.getWorld().getName(), block.getX() >> 4, block.getZ() >> 4);
        if (guildAtBlock == null) {
            return;
        }

        Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
        boolean isSameGuild = playerGuild != null && playerGuild.getId().equals(guildAtBlock.getId());

        Material type = block.getType();
        boolean isContainer = isContainerMaterial(type);
        boolean isMechanism = isMechanismMaterial(type);

        if (!isSameGuild) {
            if (isContainer || isMechanism) {
                event.setCancelled(true);
                TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
            }
            return;
        }

        // Vérification des permissions spécifiques de rang
        if (isContainer && !guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.CONTAINER)) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>Votre rang ne vous autorise pas à ouvrir les conteneurs de guilde.</red>");
        } else if (isMechanism && !guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.INTERACT)) {
            event.setCancelled(true);
            TextUtil.sendActionBar(player, "<red>Votre rang ne vous autorise pas à interagir avec ces mécanismes.</red>");
        }
    }

    /**
     * Protège les blocs de guilde contre les explosions de créatures ou de TNT.
     *
     * @param event L'événement EntityExplodeEvent.
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    public void onEntityExplode(EntityExplodeEvent event) {
        event.blockList().removeIf(block -> guildManager.isChunkClaimed(
                block.getWorld().getName(),
                block.getX() >> 4,
                block.getZ() >> 4
        ));
    }

    /**
     * Protège les blocs de guilde contre les explosions de blocs (ex: cristaux de l'End, lits dans le Nether).
     *
     * @param event L'événement BlockExplodeEvent.
     */
    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
    public void onBlockExplode(BlockExplodeEvent event) {
        event.blockList().removeIf(block -> guildManager.isChunkClaimed(
                block.getWorld().getName(),
                block.getX() >> 4,
                block.getZ() >> 4
        ));
    }

    /**
     * Détecte le passage d'une frontière de chunk et affiche une notification Action Bar lors de l'entrée/sortie de territoire.
     *
     * @param event L'événement PlayerMoveEvent.
     */
    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void onPlayerMove(PlayerMoveEvent event) {
        Location from = event.getFrom();
        Location to = event.getTo();
        if (to == null) return;

        int fromChunkX = from.getBlockX() >> 4;
        int fromChunkZ = from.getBlockZ() >> 4;
        int toChunkX = to.getBlockX() >> 4;
        int toChunkZ = to.getBlockZ() >> 4;

        if (fromChunkX == toChunkX && fromChunkZ == toChunkZ && from.getWorld().equals(to.getWorld())) {
            return; // Même chunk
        }

        String worldName = to.getWorld().getName();
        Guild previousGuild = guildManager.getGuildAt(from.getWorld().getName(), fromChunkX, fromChunkZ);
        Guild currentGuild = guildManager.getGuildAt(worldName, toChunkX, toChunkZ);

        if (Objects.equals(previousGuild, currentGuild)) {
            return;
        }

        Player player = event.getPlayer();
        if (currentGuild != null) {
            Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
            boolean isOwn = playerGuild != null && playerGuild.getId().equals(currentGuild.getId());
            String color = isOwn ? "<green>" : "<yellow>";
            TextUtil.sendActionBar(player, "<gray>Entrée dans le territoire : </gray>" + color + "<bold>" + currentGuild.getName() + "</bold></color> <dark_gray>(" + currentGuild.getTag() + ")</dark_gray>");
        } else if (previousGuild != null) {
            TextUtil.sendActionBar(player, "<gray>Vous entrez en <green>Zone Libre</green> (Wilderness).</gray>");
        }
    }

    // ==========================================
    // MÉTHODES UTILITAIRES
    // ==========================================

    private boolean isContainerMaterial(Material mat) {
        String name = mat.name();
        return name.contains("CHEST") || name.contains("BARREL") || name.contains("SHULKER") ||
                name.contains("FURNACE") || name.contains("HOPPER") || name.contains("DISPENSER") ||
                name.contains("DROPPER") || name.contains("BREWING_STAND");
    }

    private boolean isMechanismMaterial(Material mat) {
        String name = mat.name();
        return name.contains("DOOR") || name.contains("BUTTON") || name.contains("LEVER") ||
                name.contains("GATE") || name.contains("PRESSURE_PLATE");
    }
}
