/*
 * This file is part of TCSummerCamp.
 * Copyright (c) 2022 SBDevelopment - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 * Written by Stijn Bannink <stijnbannink23@gmail.com>, June 2022
 */

package nl.sbdeveloper.themeparkplus.util;

import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.regions.Polygonal2DRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * WorldGuard class to make the usage of WorldGuard easy.
 * This version only works with the latest build of WorldEdit & WorldGuard.
 *
 * <i>Note that if you do use this in one of your projects, leave this notice.</i>
 * <i>Please do credit me if you do use this in one of your projects.</i>
 *
 * @author SBDeveloper [Fixed 1.13+ support]
 */
public class WGUtil {
    public static WorldGuardPlugin wgp;
    public static WorldEditPlugin wep;

    /**
     * Enables the utility.
     */
    public static void load() {
        try {
            wep = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
            wgp = (WorldGuardPlugin) Bukkit.getPluginManager().getPlugin("WorldGuard");
        } catch (NullPointerException | ClassCastException ex) {
            throw new IllegalStateException("This plugin requires WorldEdit & WorldGuard to function!");
        }
    }

    /**
     * Create a new region.
     *
     * @param p        The player that creates the region.
     * @param regionID The ID of the new region.
     * @return The region created, or null.
     */
    public static ProtectedRegion createRegion(Player p, String regionID) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(p.getWorld()));
        if (rm == null) return null;

        if (rm.hasRegion(regionID)) return null;

        LocalSession l = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(p));
        Region s;
        try {
            s = l.getSelection(l.getSelectionWorld());
        } catch (IncompleteRegionException e) {
            return null;
        }

        ProtectedRegion region;
        if (s instanceof Polygonal2DRegion) {
            Polygonal2DRegion polySel = (Polygonal2DRegion) s;
            region = new ProtectedPolygonalRegion(regionID, polySel.getPoints(), polySel.getMinimumY(), polySel.getMaximumY());
        } else {
            region = new ProtectedCuboidRegion(regionID, s.getMinimumPoint(), s.getMaximumPoint());
        }

        rm.addRegion(region);

        return region;
    }

    /**
     * Remove a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to remove.
     * @return true if success, false if not.
     */
    public static boolean removeRegion(World world, String regionID) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return false;

        if (!rm.hasRegion(regionID)) return false;

        rm.removeRegion(regionID);
        return true;
    }

    /**
     * Get the region by its ID.
     *
     * @param world    The world to look up the region with.
     * @param regionID The ID of the region.
     * @return An optional with the region, or an empty optional if no match was found.
     */
    public static Optional<ProtectedRegion> getRegionById(World world, String regionID) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return Optional.empty();

        return Optional.ofNullable(rm.getRegion(regionID));
    }

    /**
     * Get the region by its ID.
     * This method forces the region to be in a location.
     *
     * @param loc      The location to look up the region with.
     * @param regionID The ID of the region.
     * @return An optional with the region, or an empty optional if no match was found.
     */
    public static Optional<ProtectedRegion> getRegionById(Location loc, String regionID) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(loc.getWorld()));
        if (rm == null) return Optional.empty();

        return rm.getApplicableRegions(BukkitAdapter.asBlockVector(loc)).getRegions().stream().filter(r -> r.getId().equals(regionID)).findFirst();
    }

    /**
     * Get the regions a location is in.
     *
     * @param loc The location to look up with.
     * @return The regions the location is in.
     */
    public static List<ProtectedRegion> getRegionsInLocation(Location loc) {
        List<ProtectedRegion> regions = new ArrayList<>();

        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(loc.getWorld()));
        if (rm == null) return regions;

        regions.addAll(rm.getApplicableRegions(BukkitAdapter.asBlockVector(loc)).getRegions());

        return regions;
    }

    /**
     * Check if a player is a member of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to check for.
     * @return true/false if found or not found. Null if the region does not exist.
     */
    public static Boolean isMember(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return null;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return null;

        return reg.getMembers().contains(target.getUniqueId());
    }


    /**
     * Check if a player is an owner of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to check for.
     * @return true/false if found or not found. Null if the region does not exist.
     */
    public static Boolean isOwner(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return null;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return null;

        return reg.getOwners().contains(target.getUniqueId());
    }

    /**
     * Add a player as a member of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to add.
     * @return true if success, false if not.
     */
    public static boolean addMember(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return false;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return false;

        reg.getMembers().addPlayer(target.getUniqueId());
        return true;
    }


    /**
     * Add a player as an owner of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to add.
     * @return true if success, false if not.
     */
    public static boolean addOwner(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return false;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return false;

        reg.getOwners().addPlayer(target.getUniqueId());
        return true;
    }

    /**
     * Remove a player as a member of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to remove.
     * @return true if success, false if not.
     */
    public static boolean removeMember(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return false;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return false;

        reg.getMembers().removePlayer(target.getUniqueId());
        return true;
    }

    /**
     * Remove a player as an owner of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @param target   The player to remove.
     * @return true if success, false if not.
     */
    public static boolean removeOwner(World world, String regionID, OfflinePlayer target) {
        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return false;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return false;

        reg.getOwners().removePlayer(target.getUniqueId());
        return true;
    }

    /**
     * Get the members of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @return A list of the player UUIDs of the members
     */
    public static List<UUID> getMembers(World world, String regionID) {
        List<UUID> members = new ArrayList<>();

        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return members;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return members;

        members.addAll(reg.getMembers().getUniqueIds());

        return members;
    }

    /**
     * Get the members names of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @return A list of the player names of the members
     */
    public static List<String> getMembersNames(World world, String regionID) {
        List<String> members = new ArrayList<>();

        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return members;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return members;

        members.addAll(reg.getMembers().getPlayers());

        return members;
    }

    /**
     * Get the owners of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @return A list of the player UUIDs of the owners
     */
    public static List<UUID> getOwners(World world, String regionID) {
        List<UUID> owners = new ArrayList<>();

        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return owners;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return owners;

        owners.addAll(reg.getOwners().getUniqueIds());

        return owners;
    }

    /**
     * Get the owners names of a region.
     *
     * @param world    The world the region is in.
     * @param regionID The ID of the region to check for.
     * @return A list of the player names of the owners
     */
    public static List<String> getOwnersNames(World world, String regionID) {
        List<String> owners = new ArrayList<>();

        RegionManager rm = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world));
        if (rm == null) return owners;

        ProtectedRegion reg = rm.getRegion(regionID);
        if (reg == null) return owners;

        owners.addAll(reg.getOwners().getPlayers());

        return owners;
    }
}