/*
 *   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.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.Message;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

public abstract class WinconditionBasePercent extends Wincondition implements PrintableWincondition {

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

    protected Consumer<FightTeam> checkWin = team -> {
        if (getPercent(team) >= Config.PercentWin) {
            win(Fight.getOpposite(team), "WIN_PERCENT", team.getColoredName());
        }
    };
    protected Consumer<FightTeam> postEnable = team -> {};

    public WinconditionBasePercent(Winconditions wincondition, String windescription) {
        super(windescription);

        if (Config.ActiveWinconditions.contains(wincondition)) {
            printableWinconditions.add(this);
            percentWincondition = this;
        }

        Fight.teams().forEach(team -> new TeamPercent(team, wincondition));
    }

    public Message getDisplay(FightTeam team) {
        return new Message("BAR_PERCENT", team.getPrefix() + (Math.round(10000.0 * (1.0 - getPercent(team) / Config.PercentWin)) / 100.0));
    }

    public double getPercent(FightTeam team) {
        return teamMap.get(team).getPercent();
    }

    protected int getTotalBlocks(FightTeam team) {
        return teamMap.get(team).totalBlocks;
    }

    protected int getCurrentBlocks(FightTeam team) {
        return teamMap.get(team).currentBlocks;
    }

    private class TeamPercent implements Listener {
        private final FightTeam team;

        private int totalBlocks = 0;
        private int currentBlocks = 0;
        private boolean countAnyBlock = false;

        private TeamPercent(FightTeam team, Winconditions wincondition) {
            this.team = team;

            new OneShotStateDependent(wincondition, FightState.Running, this::enable);
            new StateDependentListener(wincondition, FightState.Running, this).register();
            teamMap.put(team, this);
        }

        @EventHandler
        public void onEntityExplode(EntityExplodeEvent event) {
            if (
                    event.getEntityType() == EntityType.FIREBALL ||
                    !team.getExtendRegion().inRegion(event.getEntity().getLocation()) ||
                    (!Config.PercentEntern && !Config.EnterStages.isEmpty() && Config.EnterStages.get(0) >= Wincondition.getTimeOverCountdown().getTimeLeft())
            ) {
                return;
            }

            event.blockList().forEach(block -> {
                if (countAnyBlock || Config.PercentBlocks.contains(block.getType()) == Config.PercentBlocksWhitelist) {
                    currentBlocks--;
                }
            });

            checkWin.accept(team);
        }

        private void enable() {
            totalBlocks = 0;
            countAnyBlock = false;
            team.getSchemRegion().forEach((x, y, z) -> {
                if (Config.PercentBlocks.contains(Config.world.getBlockAt(x, y, z).getType()) == Config.PercentBlocksWhitelist)
                    totalBlocks++;
            });
            // Edge Case for DirtBlock
            if (totalBlocks == 0) {
                totalBlocks = team.getSchemRegion().volume();
                countAnyBlock = true;
            }
            currentBlocks = totalBlocks;
            postEnable.accept(team);
        }

        private double getPercent() {
            if (currentBlocks >= totalBlocks) {
                return 0;
            }
            return (totalBlocks - currentBlocks) * 100 / (double) totalBlocks;
        }
    }
}
