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

import de.steamwar.lobby.Fightserver;
import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.display.Hologram;
import de.steamwar.lobby.display.NPC;
import de.steamwar.network.packets.common.FightInfoPacket;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;

import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;

public class FightserverPortal implements PortalHandler, Comparable<FightserverPortal> {

    private static final Map<String, List<FightserverPortal>> portals = new HashMap<>();

    public static FightserverPortal findFree(String gamemode) {
        List<FightserverPortal> list = portals.getOrDefault(gamemode, Collections.emptyList());
        for (FightserverPortal portal : list) {
            if (portal.server == null) return portal;
        }
        return null;
    }

    private final Portal portal;
    private final String gamemode;
    private final int order;
    private final String target;
    private final List<Location> bluePlayers;
    private final List<Location> redPlayers;

    private Fightserver server = null;
    private PortalHandler handler = new DummyPortal();

    private final Hologram hologram;
    private final List<NPC> blueNPCs = new ArrayList<>();
    private final List<NPC> redNPCs = new ArrayList<>();

    public FightserverPortal(Map<String, Object> section, Portal portal) {
        this(
                portal,
                (String) section.get("group"),
                (int) section.get("order"),
                (String) section.get("target"),
                (List<Location>) section.getOrDefault("bluePlayers", new ArrayList<>()),
                (List<Location>) section.getOrDefault("redPlayers", new ArrayList<>())
        );
    }

    public FightserverPortal(Portal portal, String gamemode, int order, String target, List<Location> bluePlayers, List<Location> redPlayers) {
        this.portal = portal;
        this.gamemode = gamemode;
        this.order = order;
        this.target = target;
        this.bluePlayers = bluePlayers;
        this.redPlayers = redPlayers;
        hologram = new Hologram(null, portal.denormalize(new Vector(0.5, 0.5, 0.5)).toLocation(portal.getPos1().getWorld()), "", false);

        setServer(null);

        List<FightserverPortal> list = portals.computeIfAbsent(gamemode, mode -> new ArrayList<>());
        list.add(this);
        list.sort(null);
    }

    public void setServer(Fightserver server) {
        this.server = server;
        if (server == null) {
            setHandler(new TeleportPortal(portal, target));
        } else {
            setHandler(new CommandPortal("arena " + server.getServerName()));
        }
        updateText();
        updateBluePlayers();
        updateRedPlayers();
    }

    public void updateText() {
        if (server == null) {
            hologram.updateText("§7Neuen Kampf starten");
            return;
        }

        FightInfoPacket info = server.current();

        if (fightStateCountdown(info.getFightState())) {
            hologram.updateText(String.format("§7%s §e%s §7%d§8:§7%02d", server.getServerName(), fightStateMapper(info.getFightState()), info.getCountdown() / 60, info.getCountdown() % 60));
        } else {
            hologram.updateText(String.format("§7%s §e%s", server.getServerName(), fightStateMapper(info.getFightState())));
        }
    }

    public void updateBluePlayers() {
        updateNPCs(bluePlayers, blueNPCs, server != null ? server.current().getBluePlayers() : Collections.emptyList());
    }

    public void updateRedPlayers() {
        updateNPCs(redPlayers, redNPCs, server != null ? server.current().getRedPlayers() : Collections.emptyList());
    }

    private void updateNPCs(List<Location> locations, List<NPC> npcs, List<Integer> players) {
        List<SteamwarUser> remainingPlayers = players.stream().map(SteamwarUser::byId).collect(Collectors.toList());
        List<Location> remainingLocations = new ArrayList<>(locations);
        npcs.removeIf(npc -> {
            SteamwarUser user = SteamwarUser.get(npc.getUuid());
            if (remainingPlayers.contains(user)) {
                remainingPlayers.remove(user);
                remainingLocations.remove(npc.getLocation());
                return false;
            } else {
                npc.delete();
                return true;
            }
        });

        for (SteamwarUser user : remainingPlayers) {
            if (remainingLocations.isEmpty()) break;

            npcs.add(new NPC(remainingLocations.remove(0), user.getUUID(), user.getUserName()));
        }
    }

    public void addBlue(Location location) {
        bluePlayers.add(location);
        LobbySystem.config().save();
    }

    public void addRed(Location location) {
        redPlayers.add(location);
        LobbySystem.config().save();
    }

    public void removeBlue(int i) {
        bluePlayers.remove(i);
        LobbySystem.config().save();
    }

    public void removeRed(int i) {
        redPlayers.remove(i);
        LobbySystem.config().save();
    }

    private void setHandler(PortalHandler handler) {
        handler.delete();
        this.handler = handler;
    }

    @Override
    public void handle(Player player, Location from, Location to) {
        handler.handle(player, from, to);
    }

    @Override
    public void serialize(Map<String, Object> map) {
        map.put("group", gamemode);
        map.put("order", order);
        map.put("target", target);
        map.put("bluePlayers", bluePlayers);
        map.put("redPlayers", redPlayers);
    }

    @Override
    public PortalType type() {
        return PortalType.FIGHTSERVER;
    }

    @Override
    public void delete() {
        portals.get(gamemode).remove(this);
        hologram.delete();
        blueNPCs.forEach(NPC::delete);
        redNPCs.forEach(NPC::delete);
        handler.delete();
    }

    @Override
    public int compareTo(FightserverPortal other) {
        return order - other.order;
    }

    private boolean fightStateCountdown(String state) {
        switch (state) {
            case "waiting":
            case "PRE_LEADER_SETUP":
            case "end":
            case "SPECTATE":
                return false;
            default:
                return true;
        }
    }

    private String fightStateMapper(String state) {
        switch (state) {
            case "waiting":
            case "PRE_LEADER_SETUP":
                return "Warten auf Spieler";
            case "PRE_SCHEM_SETUP":
                return "Schemauswahl";
            case "POST_SCHEM_SETUP":
            case "generating_tower":
                return "Vorbereitung";
            case "PRE_RUNNING":
                return "Kampfbeginn in";
            case "fighting":
            case "RUNNING":
            case "running":
                return "Kampf läuft";
            case "end":
            case "SPECTATE":
                return "Zuschauerphase";
            default:
                LobbySystem.getInstance().getLogger().log(Level.SEVERE, "Unknown FightState " + state + " encountered");
                return "Programmierfehler";
        }
    }

    @Override
    public String toString() {
        return "Fightserver";
    }
}
