package de.viper.survivalplus.util;

import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Chest;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
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.BlockRedstoneEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.scheduler.BukkitRunnable;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class LockSystem implements Listener, CommandExecutor {

    private final SurvivalPlus plugin;
    private final Map<Location, LockData> lockedBlocks = new HashMap<>();
    private final Set<UUID> lockMode = new HashSet<>();
    private final Map<UUID, BukkitRunnable> lockTimeoutTasks = new HashMap<>();
    private final Map<UUID, Long> lastDenyMessage = new HashMap<>();

    private final File lockFile;
    private FileConfiguration lockConfig;

    public LockSystem(SurvivalPlus plugin) {
        this.plugin = plugin;
        this.lockFile = new File(plugin.getDataFolder(), "locks.yml");
        this.lockConfig = YamlConfiguration.loadConfiguration(lockFile);
        loadLocks();
    }

    public static class LockData {
        private final String ownerUUID;
        private final Set<String> friendsUUID;

        public LockData(String ownerUUID) {
            this.ownerUUID = ownerUUID;
            this.friendsUUID = new HashSet<>();
        }

        public String getOwnerUUID() {
            return ownerUUID;
        }

        public Set<String> getFriendsUUID() {
            return friendsUUID;
        }

        public void addFriend(String friendUUID) {
            friendsUUID.add(friendUUID);
        }

        public void removeFriend(String friendUUID) {
            friendsUUID.remove(friendUUID);
        }

        public boolean isFriend(String uuid) {
            return friendsUUID.contains(uuid);
        }
    }

    private void loadLocks() {
        lockedBlocks.clear();
        if (!lockFile.exists()) return;

        for (String key : lockConfig.getKeys(false)) {
            String path = key + ".";
            String owner = lockConfig.getString(path + "owner");
            List<String> friends = lockConfig.getStringList(path + "friends");

            String[] parts = key.split("_");
            if (parts.length != 4) continue;

            String worldName = parts[0];
            double x = Double.parseDouble(parts[1]);
            double y = Double.parseDouble(parts[2]);
            double z = Double.parseDouble(parts[3]);

            Location loc = new Location(plugin.getServer().getWorld(worldName), x, y, z);
            LockData lock = new LockData(owner);
            lock.getFriendsUUID().addAll(friends);

            lockedBlocks.put(loc, lock);
        }
    }

    private void saveLocks() {
        lockConfig = new YamlConfiguration();

        for (Map.Entry<Location, LockData> entry : lockedBlocks.entrySet()) {
            Location loc = entry.getKey();
            LockData lock = entry.getValue();
            String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();

            lockConfig.set(key + ".owner", lock.getOwnerUUID());
            lockConfig.set(key + ".friends", new ArrayList<>(lock.getFriendsUUID()));
        }

        try {
            lockConfig.save(lockFile);
        } catch (IOException e) {
            plugin.getLogger().severe("Konnte locks.yml nicht speichern!");
            e.printStackTrace();
        }
    }

    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;

        Block block = event.getClickedBlock();
        if (block == null) return;

        Player player = event.getPlayer();
        UUID uuid = player.getUniqueId();
        Location loc = block.getLocation();
        boolean isLockable = block.getState() instanceof InventoryHolder || block.getType().name().contains("DOOR");

        if (lockMode.contains(uuid)) {
            lockMode.remove(uuid);

            BukkitRunnable timeout = lockTimeoutTasks.remove(uuid);
            if (timeout != null) timeout.cancel();

            if (!isLockable) {
                player.sendMessage(plugin.getMessage("lock.not-lockable"));
                return;
            }

            if (lockedBlocks.containsKey(loc)) {
                player.sendMessage(plugin.getMessage("lock.already-locked"));
            } else {
                // Lock den Block
                lockedBlocks.put(loc, new LockData(uuid.toString()));
                
                // Prüfe auf Doppeltruhe und locke beide Hälften
                Block otherChest = getOtherChestHalf(block);
                if (otherChest != null) {
                    Location otherLoc = otherChest.getLocation();
                    lockedBlocks.put(otherLoc, new LockData(uuid.toString()));
                }
                
                saveLocks();
                player.sendMessage(plugin.getMessage("lock.locked"));
            }

            event.setCancelled(true);
            return;
        }

        if (!isLockable) return;
        
        // Prüfe sowohl den geklickten Block als auch die andere Hälfte bei Doppeltruhen
        if (!lockedBlocks.containsKey(loc)) {
            Block otherChest = getOtherChestHalf(block);
            if (otherChest != null) {
                loc = otherChest.getLocation();
                if (!lockedBlocks.containsKey(loc)) {
                    return;
                }
            } else {
                return;
            }
        }

        LockData lock = lockedBlocks.get(loc);
        String playerUUID = uuid.toString();

        if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) {
            return;
        }

        player.sendMessage(plugin.getMessage("lock.block-denied"));
        event.setCancelled(true);
    }

    // NEU: Verhindert das Öffnen von gelockten Türen durch Redstone (Druckplatten, Knöpfe, etc.)
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onRedstoneChange(BlockRedstoneEvent event) {
        Block block = event.getBlock();
        
        // Prüfe, ob der Block eine Tür ist
        if (!block.getType().name().contains("DOOR")) return;
        
        Location loc = block.getLocation();
        LockData lock = null;
        
        // Prüfe, ob die Tür gelockt ist
        if (lockedBlocks.containsKey(loc)) {
            lock = lockedBlocks.get(loc);
        } else {
            // Prüfe auch die andere Hälfte der Tür (oben/unten)
            Location above = loc.clone().add(0, 1, 0);
            Location below = loc.clone().add(0, -1, 0);
            
            if (lockedBlocks.containsKey(above)) {
                lock = lockedBlocks.get(above);
            } else if (lockedBlocks.containsKey(below)) {
                lock = lockedBlocks.get(below);
            }
        }
        
        // Wenn keine Lock gefunden wurde, erlaube die Änderung
        if (lock == null) return;
        
        // Wenn die Tür gelockt ist und Redstone-Signal empfängt, blockiere die Änderung
        // Es sei denn, das Signal kommt von einem berechtigten Spieler (wird über Druckplatten-Event geprüft)
        if (event.getNewCurrent() > 0) {
            event.setNewCurrent(0);
        }
    }

    // ZUSÄTZLICH: Verhindert Interaktion mit Druckplatten/Knöpfen in der Nähe von gelockten Türen
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPressurePlateInteract(PlayerInteractEvent event) {
        if (event.getAction() != Action.PHYSICAL) return;
        
        Block block = event.getClickedBlock();
        if (block == null) return;
        
        // Prüfe, ob es eine Druckplatte ist
        String typeName = block.getType().name();
        if (!typeName.contains("PRESSURE_PLATE")) return;
        
        Player player = event.getPlayer();
        UUID playerUUID = player.getUniqueId();
        
        // Prüfe alle angrenzenden Blöcke auf gelockte Türen
        for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) {
            Block relative = block.getRelative(face);
            
            // Prüfe bis zu 2 Blöcke in jede Richtung
            for (int i = 0; i < 3; i++) {
                if (relative.getType().name().contains("DOOR")) {
                    Location doorLoc = relative.getLocation();
                    
                    // Prüfe beide Türhälften
                    LockData lock = getLockData(doorLoc);
                    
                    if (lock != null && !hasAccess(player, lock)) {
                        event.setCancelled(true);
                        
                        // Anti-Spam: Nur alle 3 Sekunden eine Nachricht senden
                        long currentTime = System.currentTimeMillis();
                        Long lastMessage = lastDenyMessage.get(playerUUID);
                        
                        if (lastMessage == null || (currentTime - lastMessage) > 3000) {
                            player.sendMessage(plugin.getMessage("lock.block-denied"));
                            lastDenyMessage.put(playerUUID, currentTime);
                        }
                        return;
                    }
                }
                relative = relative.getRelative(face);
            }
        }
    }

    private boolean isLockedDoor(Location loc) {
        return lockedBlocks.containsKey(loc);
    }

    private LockData getLockData(Location loc) {
        LockData lock = lockedBlocks.get(loc);
        if (lock == null) {
            lock = lockedBlocks.get(loc.clone().add(0, 1, 0));
        }
        if (lock == null) {
            lock = lockedBlocks.get(loc.clone().add(0, -1, 0));
        }
        return lock;
    }

    private boolean hasAccess(Player player, LockData lock) {
        String playerUUID = player.getUniqueId().toString();
        return lock.getOwnerUUID().equals(playerUUID) || 
               lock.isFriend(playerUUID) || 
               player.isOp();
    }

    // Hilfsmethode: Findet die andere Hälfte einer Doppeltruhe
    private Block getOtherChestHalf(Block block) {
        if (block.getType() != Material.CHEST && block.getType() != Material.TRAPPED_CHEST) {
            return null;
        }
        
        try {
            // Versuche moderne BlockData API (1.13+)
            if (block.getBlockData() instanceof Chest) {
                Chest chestData = (Chest) block.getBlockData();
                
                // Prüfe ob es eine Doppeltruhe ist
                if (chestData.getType() == Chest.Type.SINGLE) {
                    return null;
                }
                
                // Finde die Richtung zur anderen Hälfte
                BlockFace facing = chestData.getFacing();
                BlockFace direction;
                
                if (chestData.getType() == Chest.Type.LEFT) {
                    // Linke Hälfte: Andere Hälfte ist rechts
                    direction = getRight(facing);
                } else {
                    // Rechte Hälfte: Andere Hälfte ist links
                    direction = getLeft(facing);
                }
                
                Block otherBlock = block.getRelative(direction);
                if (otherBlock.getType() == block.getType()) {
                    return otherBlock;
                }
            }
        } catch (Exception e) {
            // Fallback für ältere Versionen: Prüfe alle angrenzenden Blöcke
            for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) {
                Block relative = block.getRelative(face);
                if (relative.getType() == block.getType()) {
                    return relative;
                }
            }
        }
        
        return null;
    }
    
    private BlockFace getRight(BlockFace face) {
        switch (face) {
            case NORTH: return BlockFace.EAST;
            case EAST: return BlockFace.SOUTH;
            case SOUTH: return BlockFace.WEST;
            case WEST: return BlockFace.NORTH;
            default: return face;
        }
    }
    
    private BlockFace getLeft(BlockFace face) {
        switch (face) {
            case NORTH: return BlockFace.WEST;
            case WEST: return BlockFace.SOUTH;
            case SOUTH: return BlockFace.EAST;
            case EAST: return BlockFace.NORTH;
            default: return face;
        }
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        Player player = event.getPlayer();
        Block block = event.getBlock();
        Location loc = block.getLocation();

        if (!lockedBlocks.containsKey(loc)) return;

        LockData lock = lockedBlocks.get(loc);
        String playerUUID = player.getUniqueId().toString();

        if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) {
            // Erlaube das Brechen und entferne beide Locks bei Doppeltruhen
            lockedBlocks.remove(loc);
            Block otherChest = getOtherChestHalf(block);
            if (otherChest != null) {
                lockedBlocks.remove(otherChest.getLocation());
            }
            saveLocks();
            return;
        }

        player.sendMessage(plugin.getMessage("lock.break-denied"));
        event.setCancelled(true);
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!(sender instanceof Player)) {
            sender.sendMessage(plugin.getMessage("lock.only-players"));
            return true;
        }

        Player player = (Player) sender;
        UUID uuid = player.getUniqueId();

        // NEU: Lock als Subcommand von /sp
        if (args.length == 0 || args[0].equalsIgnoreCase("lock")) {
            if (lockMode.contains(uuid)) {
                player.sendMessage(plugin.getMessage("lock.mode-already"));
                return true;
            }

            lockMode.add(uuid);
            player.sendMessage(plugin.getMessage("lock.mode-start"));

            BukkitRunnable timeout = new BukkitRunnable() {
                @Override
                public void run() {
                    if (lockMode.remove(uuid)) {
                        player.sendMessage(plugin.getMessage("lock.mode-timeout"));
                        lockTimeoutTasks.remove(uuid);
                    }
                }
            };
            timeout.runTaskLater(plugin, 20 * 30); // 30 Sekunden
            lockTimeoutTasks.put(uuid, timeout);
            return true;
        }

        String sub = args[0].toLowerCase();
        Block targetBlock = player.getTargetBlockExact(5);
        if (targetBlock == null) {
            player.sendMessage(plugin.getMessage("lock.no-target-block"));
            return true;
        }

        Location loc = targetBlock.getLocation();

        switch (sub) {
            case "unlock":
                LockData lock = lockedBlocks.get(loc);
                if (lock == null) {
                    player.sendMessage(plugin.getMessage("lock.not-locked"));
                } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) {
                    player.sendMessage(plugin.getMessage("lock.no-permission-unlock"));
                } else {
                    lockedBlocks.remove(loc);
                    
                    // Entferne auch Lock von der anderen Hälfte bei Doppeltruhen
                    Block otherChest = getOtherChestHalf(targetBlock);
                    if (otherChest != null) {
                        lockedBlocks.remove(otherChest.getLocation());
                    }
                    
                    saveLocks();
                    player.sendMessage(plugin.getMessage("lock.unlocked"));
                }
                break;

            case "friendadd":
                if (args.length < 2) {
                    player.sendMessage(plugin.getMessage("lock.friendadd.usage"));
                    return true;
                }
                lock = lockedBlocks.get(loc);
                if (lock == null) {
                    player.sendMessage(plugin.getMessage("lock.not-locked"));
                } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) {
                    player.sendMessage(plugin.getMessage("lock.no-permission-friends"));
                } else {
                    Player friend = player.getServer().getPlayer(args[1]);
                    if (friend == null) {
                        player.sendMessage(plugin.getMessage("lock.friendadd.not-found"));
                    } else {
                        lock.addFriend(friend.getUniqueId().toString());
                        
                        // Füge Friend auch zur anderen Hälfte bei Doppeltruhen hinzu
                        Block otherChest = getOtherChestHalf(targetBlock);
                        if (otherChest != null) {
                            LockData otherLock = lockedBlocks.get(otherChest.getLocation());
                            if (otherLock != null) {
                                otherLock.addFriend(friend.getUniqueId().toString());
                            }
                        }
                        
                        saveLocks();
                        player.sendMessage(plugin.getMessage("lock.friendadd.success").replace("{player}", friend.getName()));
                    }
                }
                break;

            case "friendremove":
                if (args.length < 2) {
                    player.sendMessage(plugin.getMessage("lock.friendremove.usage"));
                    return true;
                }
                lock = lockedBlocks.get(loc);
                if (lock == null) {
                    player.sendMessage(plugin.getMessage("lock.not-locked"));
                } else if (!lock.getOwnerUUID().equals(uuid.toString()) && !player.isOp()) {
                    player.sendMessage(plugin.getMessage("lock.no-permission-friends"));
                } else {
                    Player friend = player.getServer().getPlayer(args[1]);
                    if (friend == null) {
                        player.sendMessage(plugin.getMessage("lock.friendremove.not-found"));
                    } else {
                        lock.removeFriend(friend.getUniqueId().toString());
                        
                        // Entferne Friend auch von der anderen Hälfte bei Doppeltruhen
                        Block otherChest = getOtherChestHalf(targetBlock);
                        if (otherChest != null) {
                            LockData otherLock = lockedBlocks.get(otherChest.getLocation());
                            if (otherLock != null) {
                                otherLock.removeFriend(friend.getUniqueId().toString());
                            }
                        }
                        
                        saveLocks();
                        player.sendMessage(plugin.getMessage("lock.friendremove.success").replace("{player}", friend.getName()));
                    }
                }
                break;

            default:
                player.sendMessage(plugin.getMessage("lock.unknown-subcommand"));
                break;
        }

        return true;
    }
}