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

import de.steamwar.core.TPSWatcher;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentCountdown;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.winconditions.Wincondition;
import de.steamwar.linkage.Linked;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.stream.Collectors;

@Linked
public class FightUI {

    @Getter
    private static FightUI instance;

    public FightUI() {
        new StateDependentTask(ArenaMode.AntiReplay, FightState.All, this::update, 20, 20);
        new StateDependentTask(ArenaMode.All, FightState.All, this::sendToPlayers, 5, 5);
        new StateDependentTask(ArenaMode.All, FightState.All, this::tpsWatcher, 20, 20);
        new OneShotStateDependent(ArenaMode.AntiReplay, FightState.PreRunning, () -> addSubtitle("UI_PRE_RUNNING"));
        new OneShotStateDependent(ArenaMode.AntiReplay, FightState.Running, () -> addSubtitle("UI_RUNNING"));
        instance = this;
    }

    private void update() {
        final FightTeam blue = Fight.getBlueTeam();
        final FightTeam red = Fight.getRedTeam();

        double blueHearts = blue.getCurrentHearts();
        double redHearts = red.getCurrentHearts();
        double totalHearts = blueHearts + redHearts;
        if (redHearts == 0.0 || blueHearts == 0.0) {
            blueHearts = 0.5;
            redHearts = 0.5;
            totalHearts = 1.0;
        }

        int timeLeft = StateDependentCountdown.getMainCountdown() != null ? StateDependentCountdown.getMainCountdown().getTimeLeft() : 0;
        String time = "§7" + (timeLeft / 60) + "§8:§7" + (timeLeft % 60 < 10 ? "0" : "") + (timeLeft % 60);
        String bluePlayers = blue.getPrefix() + blue.getAlivePlayers() + "§8/§7" + blue.getPlayerCount();
        String redPlayers = red.getPrefix() + red.getAlivePlayers() + "§8/§7" + red.getPlayerCount();
        List<Message> redAdditional = Wincondition.getPrintableWinconditions().stream().map(w -> w.getDisplay(red)).collect(Collectors.toList());
        List<Message> blueAdditional = Wincondition.getPrintableWinconditions().stream().map(w -> w.getDisplay(blue)).collect(Collectors.toList());

        Generator generator;
        switch (FightState.getFightState()) {
            case PRE_LEADER_SETUP:
                generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_LEADER");
                break;
            case PRE_SCHEM_SETUP:
                generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_SCHEM", time, l.getColoredName(), r.getColoredName());
                break;
            case POST_SCHEM_SETUP:
                generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PREPARE", time, l.getColoredName(), r.getColoredName(), lP, rP);
                break;
            case PRE_RUNNING:
                generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_RUNNING", time, l.getColoredName(), r.getColoredName(), lP, rP);
                break;
            case RUNNING:
                generator = (l, r, lP, rP, lW, rW) -> {
                    List<Object> params = new ArrayList<>();
                    params.add(time);
                    params.add(l.getColoredName());
                    params.add(r.getColoredName());
                    params.add(lP);
                    params.add(rP);
                    for (int i = 0; i < lW.size(); i++) {
                        params.add(lW.get(i));
                        params.add(rW.get(i));
                    }
                    return new Message("BAR_RUNNING" + lW.size(), params.toArray());
                };
                break;
            case SPECTATE:
            default:
                generator = (l, r, lP, rP, lW, rW) -> {
                    if (FightSystem.getLastWinner() == null) {
                        return new Message("BAR_TIE", time, l.getColoredName(), r.getColoredName());
                    } else {
                        return new Message("BAR_WIN", time, l.getColoredName(), r.getColoredName(), FightSystem.getLastWinner().getColoredName());
                    }
                };
                break;
        }

        setBossbar(
                blueHearts / totalHearts, redHearts / totalHearts,
                generator.gen(blue, red, bluePlayers, redPlayers, blueAdditional, redAdditional),
                generator.gen(red, blue, redPlayers, bluePlayers, redAdditional, blueAdditional)
        );
    }

    private interface Generator {
        Message gen(FightTeam l, FightTeam r, String lPlayers, String rPlayers, List<Message> lWinconditions, List<Message> rWinconditions);
    }

    public void setBossbar(double leftBlueProgress, double leftRedProgress, String leftBlueText, String leftRedText) {
        setBossbar(leftBlueProgress, leftRedProgress, new Message("OLD_STRING", leftBlueText), new Message("OLD_STRING", leftRedText));
    }

    public void setBossbar(double leftBlueProgress, double leftRedProgress, Message leftBlueText, Message leftRedText) {
        GlobalRecorder.getInstance().bossBar(leftBlueProgress, leftRedProgress, leftBlueText, leftRedText);
        BossBarType.BLUE_LEFT.progress = leftBlueProgress;
        BossBarType.RED_LEFT.progress = leftRedProgress;
        BossBarType.BLUE_LEFT.text = leftBlueText;
        BossBarType.RED_LEFT.text = leftRedText;
    }

    private void sendToPlayers() {
        Bukkit.getOnlinePlayers().forEach(player -> {
            BossBarType bar = BossBarType.byAngle(CraftbukkitWrapper.impl.headRotation(player));

            Object[] params = bar.text.getParams().clone();
            for (int i = 0; i < params.length; i++) {
                if (params[i] instanceof Message) {
                    Message msg = (Message) params[i];
                    params[i] = FightSystem.getMessage().parse(msg.getMsg(), player, msg.getParams());
                }
            }

            BountifulWrapper.impl.sendBar(player, bar.team, bar.progress, FightSystem.getMessage().parse(bar.text.getMsg(), player, params));
        });
    }

    private void tpsWatcher() {
        double tps = TPSWatcher.getTPS();
        if (tps < 15.0) FightSystem.getMessage().broadcastActionbar("TPS_WARNING", tps);
    }

    public enum BossBarType {
        BLUE_LEFT(Fight.getBlueTeam(), 345, 165),
        RED_LEFT(Fight.getRedTeam(), 165, 345);

        private static final boolean BLUE_NEG_Z = Config.blueNegZ();

        private final double minAngle;
        private final double maxAngle;

        private final FightTeam team;
        private double progress = 0.5;
        private Message text = new Message("OLD_STRING", "§7");

        BossBarType(FightTeam team, double minAngle, double maxAngle) {
            this.team = team;
            this.minAngle = minAngle;
            this.maxAngle = maxAngle;
        }

        private static BossBarType byAngle(double angle) {
            if (BLUE_NEG_Z) angle += 180;
            angle = ((angle % 360) + 360) % 360;

            for (BossBarType type : values()) {
                if (type.minAngle < type.maxAngle ? type.minAngle <= angle && angle < type.maxAngle : type.minAngle <= angle || angle < type.maxAngle) {
                    return type;
                }
            }

            throw new SecurityException("No boss bar for angle " + angle + " found");
        }
    }


    private static final Queue<Message> queue = new LinkedList<>();
    private static boolean subtitleScheduled = false;

    public static void printWin(FightTeam winner, String subtitle, Object... params) {
        queue.clear();

        FightSystem.getPlugin().getLogger().log(Level.INFO, winner + " " + FightSystem.getMessage().parse(subtitle, null, params));
        GlobalRecorder.getInstance().winMessage(winner, subtitle, params);

        Bukkit.getOnlinePlayers().forEach(Player::resetTitle);
        if (winner != null) {
            Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_WIN", p, winner.getColor(), winner.getName()), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
        } else {
            Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_DRAW", p), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
        }
    }

    public static void addSubtitle(String msg, Object... params) {
        if (FightState.Spectate.contains(FightState.getFightState())) return;

        Message message = new Message(msg, params);
        queue.add(message);
        GlobalRecorder.getInstance().subtitle(message);
        FightSystem.getPlugin().getLogger().log(Level.INFO, FightSystem.getMessage().parse(msg, null, params));

        if (!subtitleScheduled) printSubtitle();
    }

    private static void printSubtitle() {
        if (queue.isEmpty()) {
            subtitleScheduled = false;
            return;
        }
        Message message = queue.poll();

        Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, " ", FightSystem.getMessage().parse(message.getMsg(), p, message.getParams()), 5, 40, 5));
        Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), FightUI::printSubtitle, 50);
        subtitleScheduled = true;
    }
}
