/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2023  SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.bausystem.features.world;

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import de.steamwar.sql.BauweltMember;
import de.steamwar.techhider.TechHider;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
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.BlockBreakEvent;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.*;
import org.bukkit.util.Vector;

import java.util.HashSet;
import java.util.Set;

@Linked
public class SpectatorListener implements Listener {

    private static final Set<Player> NO_TECHHIDER = new HashSet<>();

    static {
        Set<Material> materials = new HashSet<>();
        materials.add(Material.REDSTONE_WIRE);
        materials.add(Material.REDSTONE_TORCH);
        materials.add(Material.REDSTONE_BLOCK);
        materials.add(Material.REPEATER);
        materials.add(Material.COMPARATOR);
        materials.add(Material.LEVER);
        for (Material value : Material.values()) {
            if (value.isLegacy()) continue;
            if (value.name().endsWith("_BUTTON")) {
                materials.add(value);
            }
            if (value.name().endsWith("_PRESSURE_PLATE")) {
                materials.add(value);
            }
            if (value.name().endsWith("_SIGN")) {
                materials.add(value);
            }
        }
        materials.add(SWItem.getMaterial("SCULK_SENSOR"));
        materials.add(SWItem.getMaterial("CALIBRATED_SCULK_SENSOR"));
        materials.add(SWItem.getMaterial("SCULK_SHRIEKER"));
        materials.add(SWItem.getMaterial("AMETHYST_BLOCK"));
        materials.add(SWItem.getMaterial("AMETHYST_CLUSTER"));
        materials.add(SWItem.getMaterial("SMALL_AMETHYST_BUG"));
        materials.add(SWItem.getMaterial("MEDIUM_AMETHYST_BUG"));
        materials.add(SWItem.getMaterial("LARGE_AMETHYST_BUG"));
        materials.add(Material.TRIPWIRE_HOOK);
        materials.add(Material.TRIPWIRE);
        materials.add(Material.DAYLIGHT_DETECTOR);
        materials.add(SWItem.getMaterial("LIGHTNING_ROD"));
        materials.add(Material.PISTON);
        materials.add(Material.PISTON_HEAD);
        materials.add(Material.MOVING_PISTON);
        materials.add(Material.STICKY_PISTON);
        materials.add(Material.SLIME_BLOCK);
        materials.add(Material.HONEY_BLOCK);
        materials.add(Material.OBSERVER);
        materials.add(Material.RAIL);
        materials.add(Material.POWERED_RAIL);
        materials.add(Material.DETECTOR_RAIL);
        materials.add(Material.ACTIVATOR_RAIL);
        materials.add(Material.TNT);
        materials.add(Material.REDSTONE_ORE);
        materials.add(SWItem.getMaterial("SCAFFOLDING"));
        materials.add(Material.WATER);
        materials.remove(Material.BARRIER);
        materials.remove(Material.STONE);
        TechHider techHider = new TechHider((TechHider.LocationEvaluator) (player, i, i1) -> {
            return Permission.BUILD.hasPermission(player) || Permission.isTempOnlySpectator(player) || NO_TECHHIDER.contains(player);
        }, Material.END_STONE, materials, new HashSet<>());
        techHider.enable();
    }

    public static void toggleNoTechHider(Player player) {
        if (NO_TECHHIDER.contains(player)) {
            NO_TECHHIDER.remove(player);
        } else {
            NO_TECHHIDER.add(player);
        }
        resendChunks(player);
    }

    private static void resendChunks(Player player) {
        Location location = player.getLocation().clone();
        Vector velocity = player.getVelocity().clone();
        player.teleport(location.clone().add(16.0 * player.getClientViewDistance() + 32, 0, 16.0 * player.getClientViewDistance() + 32));
        Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
            player.teleport(location);
            player.setVelocity(velocity);
        }, 5);
    }

    private boolean anySupervisorOnline(Player player) {
        return Bukkit.getOnlinePlayers().stream().filter(p -> p != player).anyMatch(Permission.SUPERVISOR::hasPermission);
    }

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        if (BauSystem.DEV_SERVER) return;
        if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
            return;
        }
        BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId());
        if (bauweltMember == null) {
            event.getPlayer().kickPlayer("");
            return;
        }
        if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
            return;
        }
        if (!anySupervisorOnline(null)) {
            Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
                event.getPlayer().kickPlayer("");
            }, 1);
        }
    }

    @EventHandler
    public void onBauMemberUpdate(BauMemberUpdateEvent event) {
        if (!anySupervisorOnline(null) && !BauSystem.DEV_SERVER) {
            Bukkit.getOnlinePlayers().forEach(player -> {
                player.kickPlayer("");
            });
            return;
        }

        event.getChanged().forEach(player -> {
            NO_TECHHIDER.remove(player);
            if (Permission.isTempOnlySpectator(player)) return;
            resendChunks(player);
        });
    }

    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        NO_TECHHIDER.remove(event.getPlayer());
        if (BauSystem.DEV_SERVER) return;
        if (!anySupervisorOnline(event.getPlayer())) {
            Bukkit.getOnlinePlayers().forEach(player -> {
                player.kickPlayer("");
            });
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
        if (event.getMessage().startsWith("/schem save") || event.getMessage().startsWith("//schem save") || event.getMessage().startsWith("/schematic save") || event.getMessage().startsWith("//schematic save")) {
            if (!Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
                event.setCancelled(true);
                event.setMessage("/");
            }
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onBlockMultiPlace(BlockMultiPlaceEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onBlockCanBuild(BlockCanBuildEvent event) {
        if (event.getPlayer() == null) return;
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setBuildable(false);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onBlockPlace(BlockPlaceEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onBlockBreak(BlockBreakEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerBucketFill(PlayerBucketFillEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onPlayerDropItem(PlayerDropItemEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) {
            event.setCancelled(true);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void onEntityPickupItem(EntityPickupItemEvent event) {
        if (!(event.getEntity() instanceof Player)) return;
        if (!Permission.BUILD.hasPermission((Player) event.getEntity())) {
            event.setCancelled(true);
        }
    }

    @Linked
    @MinVersion(20)
    public static class SpectatorListener20 implements Listener {
        @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
        public void onPlayerBucketEntity(PlayerBucketEntityEvent event) {
            if (!Permission.BUILD.hasPermission(event.getPlayer())) {
                event.setCancelled(true);
            }
        }
    }
}
