/*
 *   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.fightsystem.winconditions;

import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.countdown.TimeOverCountdown;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCountdown;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.Message;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.HashMap;
import java.util.Map;

@Linked
public class WinconditionPoints extends WinconditionBasePercent implements Listener {

    private final Map<FightTeam, TeamPoints> teamMap = new HashMap<>();

    public WinconditionPoints() {
        super(Winconditions.POINTS, "Points");

        checkWin = team -> {
        };
        postEnable = this::pointInit;

        Fight.teams().forEach(team -> teamMap.put(team, new TeamPoints(team)));

        new StateDependentListener(Winconditions.POINTS, FightState.Ingame, this);

        if (Config.GameModeConfig.WinConditions.contains(Winconditions.POINTS)) {
            timeOverCountdown = new StateDependentCountdown(Winconditions.POINTS, FightState.Running, new TimeOverCountdown(this::timeOver));
        }
    }

    private void timeOver() {
        comparisonWin(team -> teamMap.get(team).getPoints(), "WIN_POINTS", "WIN_POINTS_EQUAL");
    }

    @EventHandler
    public void handlePlayerDeath(PlayerDeathEvent event) {
        handleDeath(event.getEntity().getPlayer());
    }

    @EventHandler
    public void handlePlayerQuit(PlayerQuitEvent event) {
        handleDeath(event.getPlayer());
    }

    private void handleDeath(Player player) {
        FightTeam team = isTarget(player);
        if (team == null) return;

        TeamPoints enemy = teamMap.get(Fight.getOpposite(team));
        if (team.isPlayerLeader(player)) {
            enemy.points += 500;
            timeOver();
        } else if (team.getPlayers().size() <= 5) {
            enemy.points += 300;
        } else {
            enemy.points += 200;
        }
    }

    private void pointInit(FightTeam team) {
        TeamPoints opponent = teamMap.get(Fight.getOpposite(team));
        if (getTotalBlocks(team) == 0) return;

        teamMap.get(team).setup(getTotalBlocks(Fight.getOpposite(team)));
        opponent.setup(getTotalBlocks(team));
    }

    @Override
    public Message getDisplay(FightTeam team) {
        return new Message("BAR_POINTS", team.getPrefix() + teamMap.get(team).getPoints());
    }

    private class TeamPoints {
        private static final int MAX_POINTS = 2000;

        private final FightTeam team;
        private double factor;
        private int points;

        TeamPoints(FightTeam team) {
            this.team = team;
        }

        private void setup(int enemyBlocks) {
            points = 0;
            if (enemyBlocks < getTotalBlocks(team)) {
                this.factor = 100; //Original mit 20 (20% = 0.2 ergeben 2000 Punkte
            } else {
                double f = 100.0 * getTotalBlocks(team) / enemyBlocks;

                if (f > 100) {
                    f = 100;
                } else if (f < 40) {
                    f = 40;
                }

                this.factor = f;
            }
        }

        public int getPoints() {
            int damagePoints = (int) (getPercent(Fight.getOpposite(team)) * factor);
            if (damagePoints > MAX_POINTS) damagePoints = MAX_POINTS;
            return points + damagePoints;
        }
    }
}
