/*
 * This file is a part of the SteamWar software.
 *
 * Copyright (C) 2022  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.lobby.team;

import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.display.NPC;
import de.steamwar.lobby.listener.BasicListener;
import de.steamwar.sql.SteamwarUser;
import lombok.AllArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityInteractEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.stream.Collectors;

public class TeamPlayer extends BasicListener {

    private static final List<Cuboid> cuboids = new ArrayList<>();

    static {
        cuboids.add(new Cuboid(2743, 58, 2306, 2744, 59, 1316));
    }

    private static final World world = Bukkit.getWorlds().get(0);
    private static final Map<String, NPC> entities = new HashMap<>();
    private static final Map<String, Villager> villagers = new HashMap<>();

    private Set<Player> players = new HashSet<>();

    private Random random = new Random();

    public static void spawnTeamPlayer(World world, SteamwarUser steamwarUser) {
        Location location = new Location(world, 2790.5, 69, 1311.5);
        String name = steamwarUser.getUserName();
        NPC npc = new NPC(location, steamwarUser.getUUID(), name);
        Villager villager = (Villager) world.spawnEntity(location, EntityType.VILLAGER);
        villager.setSilent(true);
        villager.setInvulnerable(true);
        villager.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 1, false, false, false));
        villager.setCustomName(name);
        villager.setProfession(Villager.Profession.NONE);
        villagers.put(name, villager);
        entities.put(name, npc);
    }

    {
        SteamwarUser.getServerTeam().forEach(user -> {
            spawnTeamPlayer(world, user);
        });

        AtomicInteger count = new AtomicInteger();
        LobbySystem.getPlugin().getLogger().log(Level.INFO, "Loaded " + entities.size() + " team players");
        Bukkit.getScheduler().runTaskTimer(LobbySystem.getPlugin(), () -> {
            Collection<Villager> active = world.getEntitiesByClass(Villager.class);
            if (active.size() > entities.size()) {
                Set<String> duplicates = new HashSet<>();
                active.stream()
                        .filter(villager -> !duplicates.add(villager.getCustomName()))
                        .forEach(Entity::remove);
            }

            count.incrementAndGet();
            if (count.get() % (20 * 60 * 60) == 0) {
                count.set(0);
                List<SteamwarUser> steamwarUsers = SteamwarUser.getServerTeam();
                AtomicInteger added = new AtomicInteger();
                steamwarUsers.forEach(user -> {
                    if (!entities.containsKey(user.getUserName())) {
                        spawnTeamPlayer(world, user);
                        added.incrementAndGet();
                    }
                });
                AtomicInteger removed = new AtomicInteger();
                List<String> names = steamwarUsers.stream().map(SteamwarUser::getUserName).collect(Collectors.toList());
                new HashSet<>(entities.keySet()).stream().filter(name -> !names.contains(name)).forEach(name -> {
                    villagers.remove(name).remove();
                    entities.remove(name).delete();
                    removed.incrementAndGet();
                });
                if (added.get() > 0 || removed.get() > 0) {
                    LobbySystem.getPlugin().getLogger().log(Level.INFO, "Loaded " + added.get() + " team players, removed " + removed.get() + " team players");
                }
            }
            world.getEntitiesByClasses(Villager.class).forEach(entity -> {
                NPC npc = entities.get(entity.getName());
                if (npc != null) {
                    if (illegalLocation(entity.getLocation())) {
                        entity.teleport(npc.getLocation());
                        return;
                    }
                    npc.setLocation(entity.getLocation());
                }
            });
        }, 1L, 1L);
    }

    private boolean illegalLocation(Location location) {
        for (Cuboid cuboid : cuboids) {
            if (cuboid.contains(location)) {
                return true;
            }
        }
        return false;
    }

    private String parseRandomMessage(Player player, SteamwarUser target, String message) throws MissingResourceException {
        return LobbySystem.getMessage().parsePrefixed(message + random.nextInt(6), player, target.getUserName(), target.prefix().getColorCode() + target.prefix().getChatPrefix());
    }

    @EventHandler
    public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
        if (!(event.getRightClicked() instanceof Villager)) {
            return;
        }
        if (!players.add(event.getPlayer())) {
            players.remove(event.getPlayer());
            return;
        }

        event.setCancelled(true);

        SteamwarUser target = SteamwarUser.get(event.getRightClicked().getName());
        String message;
        try {
            message = parseRandomMessage(event.getPlayer(), target, "NPC_CHAT_" + target.getId() + "_");
        } catch (MissingResourceException e) {
            message = parseRandomMessage(event.getPlayer(), target, "NPC_CHAT_");
        }
        event.getPlayer().sendMessage(message);
    }

    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        players.remove(event.getPlayer());
    }

    @EventHandler
    public void onEntityInteract(EntityInteractEvent event) {
        if (event.getEntityType() == EntityType.VILLAGER) {
            event.setCancelled(true);
        }
    }

    @EventHandler
    public void onEntityDamage(EntityDamageEvent event) {
        if (event.getEntityType() == EntityType.VILLAGER) {
            event.setCancelled(true);
        }
    }

    @AllArgsConstructor
    private static class Cuboid {
        private double x1;
        private double y1;
        private double z1;
        private double x2;
        private double y2;
        private double z2;

        public boolean contains(Location location) {
            return location.getX() >= x1 && location.getX() <= x2 && location.getY() >= y1 && location.getY() <= y2 && location.getZ() >= z1 && location.getZ() <= z2;
        }
    }
}
