/*
 *   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.bausystem.features.tpslimit;

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.bausystem.utils.TickEndEvent;
import de.steamwar.bausystem.utils.TickManager;
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
import de.steamwar.bausystem.utils.bossbar.BossBarService;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import de.steamwar.core.TPSWatcher;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.boss.BarColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;

import java.util.Arrays;

@Linked
public class TPSSystem implements Listener {

    public TPSSystem() {
        if (TickManager.impl.canFreeze()) {
            new TPSFreezeCommand();
            new TickFreezeCommand();
            new TickStepCommand();
        }
        new TPSLimitCommand();
        new TickLimitCommand();
        new TPSWarpCommand();
        new TickWarpCommand();
        if (TickManager.impl.canFreeze()) {
            new TickWarpingCommand();
        }
        new Tick21Command();
        new TPSDefaultCommand();
        new TickDefaultCommand();
        new TPSBaseCommand();
        new TickBaseCommand();
        Bukkit.getPluginManager().registerEvents(TickManager.impl, BauSystem.getInstance());
    }

    @EventHandler
    public void onTickEnd(TickEndEvent event) {
        bossbar();
    }

    private void bossbar() {
        if ((TickManager.impl.isStepping() || TickManager.impl.isSprinting()) && TickManager.impl.getRemainingTicks() > 0) {
            Bukkit.getOnlinePlayers().forEach(player -> {
                BauSystemBossbar bossbar = BossBarService.instance.get(player, Region.getGlobalRegion(), "TickStep");
                bossbar.setColor(BarColor.YELLOW);
                bossbar.setTitle(BauSystem.MESSAGE.parse("TICK_BOSSBAR", player, TickManager.impl.getDoneTicks(), TickManager.impl.getTotalTicks()));
                bossbar.setProgress(TickManager.impl.getDoneTicks() / (double) TickManager.impl.getTotalTicks());
            });
        } else {
            Bukkit.getOnlinePlayers().forEach(player -> {
                BossBarService.instance.remove(player, Region.getGlobalRegion(), "TickStep");
            });
        }
    }

    public static void sendTickRateChange() {
        Bukkit.getOnlinePlayers().forEach(player -> {
            if (TickManager.impl.isFrozen()) {
                SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("TPSLIMIT_FROZEN", player));
            } else {
                SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("TPSLIMIT_SET", player, TickManager.impl.getTickRate()));
            }
        });
    }

    private class TPSBaseCommand extends SWCommand {

        private TPSBaseCommand() {
            super("tpslimit");
            setMessage(BauSystem.MESSAGE);
            addDefaultHelpMessage("TPSLIMIT_HELP");
        }
    }

    @AbstractSWCommand.PartOf(TPSBaseCommand.class)
    private class TPSFreezeCommand extends SWCommand {

        private TPSFreezeCommand() {
            super("");
        }

        @Register(value = "0", description = "TPSLIMIT_FREEZE_HELP")
        public void freeze(@Validator Player player) {
            TickManager.impl.setFreeze(true);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TPSBaseCommand.class)
    private class TPSLimitCommand extends SWCommand {

        private TPSLimitCommand() {
            super("");
        }

        @Register(description = "TPSLIMIT_LIMIT_HELP")
        public void limit(@Validator Player player, @Min(doubleValue = 0.5) @Max(doubleValue = 20.0) float tpsLimit) {
            TickManager.impl.setTickRate(tpsLimit);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TPSBaseCommand.class)
    private class TPSWarpCommand extends SWCommand {

        private TPSWarpCommand() {
            super("");
        }

        @Register(description = "TPSLIMIT_WARP_HELP")
        public void warp(@Validator Player player, @Min(doubleValue = 20.0, inclusive = false) float tpsLimit) {
            TickManager.impl.setTickRate(tpsLimit);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TPSBaseCommand.class)
    private class TPSDefaultCommand extends SWCommand {

        private TPSDefaultCommand() {
            super("");
        }

        @Register(description = "TPSLIMIT_HELP")
        public void currentLimit(Player player) {
            BauSystem.MESSAGE.send("TPSLIMIT_CURRENT", player, TickManager.impl.getTickRate());
        }

        @Register(value = "default", description = "TPSLIMIT_DEFAULT_HELP")
        public void reset(@Validator Player player) {
            TickManager.impl.setTickRate(20.0F);
            sendTickRateChange();
        }
    }

    private class TickBaseCommand extends SWCommand {

        public TickBaseCommand() {
            super("tick");
            setMessage(BauSystem.MESSAGE);
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickFreezeCommand extends SWCommand {

        private TickFreezeCommand() {
            super("");
        }

        @Register(value = {"rate", "0"}, description = "TICK_FREEZE_HELP")
        @Register(value = "freeze", description = "TICK_FREEZE_HELP_2")
        public void freeze(@Validator Player player) {
            TickManager.impl.setFreeze(true);
            sendTickRateChange();
        }

        @Register(value = "unfreeze", description = "TICK_UNFREEZE_HELP")
        public void unfreeze(@Validator Player player) {
            TickManager.impl.setTickRate(20.0F);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickStepCommand extends SWCommand {

        public TickStepCommand() {
            super("");
        }

        @Register(value = "step", description = "TICK_STEPPING_HELP")
        public void step(@Validator Player player, @Min(intValue = 1) @OptionalValue("1") int steps) {
            TickManager.impl.stepTicks(steps);
            sendTickRateChange();
            bossbar();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickWarpingCommand extends SWCommand {

        public TickWarpingCommand() {
            super("");
        }

        @Register(value = "warp", description = "TICK_WARPING_HELP")
        public void warp(@Validator Player player, @Min(intValue = 1) @OptionalValue("1") int steps) {
            TickManager.impl.sprintTicks(steps);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickLimitCommand extends SWCommand {

        private TickLimitCommand() {
            super("");
        }

        @Register(value = "rate", description = "TICK_LIMIT_HELP")
        public void limit(@Validator Player player, @Min(doubleValue = 0.5, inclusive = false) @Max(doubleValue = 20.0) float tpsLimit) {
            TickManager.impl.setTickRate(tpsLimit);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickWarpCommand extends SWCommand {

        private TickWarpCommand() {
            super("");
        }

        @Register(value = "rate", description = "TICK_WARP_HELP")
        public void warp(@Validator Player player, @Min(doubleValue = 20.0, inclusive = false) float tpsLimit) {
            TickManager.impl.setTickRate(tpsLimit);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class TickDefaultCommand extends SWCommand {

        private TickDefaultCommand() {
            super("");
        }

        @Register(value = "rate", description = "TICK_HELP")
        public void currentLimit(Player player) {
            BauSystem.MESSAGE.send("TPSLIMIT_CURRENT", player, TickManager.impl.getTickRate());
        }

        @Register(value = {"rate", "default"}, description = "TICK_DEFAULT_HELP")
        public void reset(@Validator Player player) {
            TickManager.impl.setTickRate(20.0F);
            sendTickRateChange();
        }
    }

    @AbstractSWCommand.PartOf(TickBaseCommand.class)
    private class Tick21Command extends SWCommand {

        private Tick21Command() {
            super("");
        }

        @Register(value = "normalclient")
        public void smooth(@Validator Player player) {
            TickManager.impl.setBlockTpsPacket(true);
        }

        @Register(value = "slowclient")
        public void unsmooth(@Validator Player player) {
            TickManager.impl.setBlockTpsPacket(false);
        }
    }

    @Linked
    public static class TPSScoreboardElement implements ScoreboardElement {

        @LinkedInstance
        public TPSSystem tpsSystem;

        @Override
        public ScoreboardGroup getGroup() {
            return ScoreboardGroup.FOOTER;
        }

        @Override
        public int order() {
            return 0;
        }

        @Override
        public String get(Region region, Player p) {
            boolean isWarping = TickManager.impl.isSprinting();
            boolean isFrozen = TickManager.impl.isFrozen();

            if (tpsSystem != null && isWarping) {
                long time = System.currentTimeMillis() % 1000;
                if (time < 250) {
                    return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §7•••";
                } else if (time < 500) {
                    return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §e•§7••";
                } else if (time < 750) {
                    return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §7•§e•§7•";
                } else {
                    return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §7••§e•";
                }
            } else if (isFrozen) {
                return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: " + BauSystem.MESSAGE.parse("SCOREBOARD_TPS_FROZEN", p);
            } else {
                return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: " + tpsColor() + TPSWatcher.getTPSUnlimited(TPSWatcher.TPSType.ONE_SECOND) + tpsLimit();
            }
        }

        private String tpsColor() {
            double tps = TPSWatcher.getTPSUnlimited(TPSWatcher.TPSType.ONE_SECOND);
            if (tps > TickManager.impl.getTickRate() * 0.9) {
                return "§a";
            }
            if (tps > TickManager.impl.getTickRate() * 0.5) {
                return "§e";
            }
            return "§c";
        }

        private String tpsLimit() {
            if (TickManager.impl.getTickRate() == 20) {
                return "";
            }
            return "§8/§7" + TickManager.impl.getTickRate();
        }
    }

    @Linked
    public static class TPSSystemBauGuiItem extends BauGuiItem {

        @LinkedInstance
        public TPSSystem tpsSystem;

        public TPSSystemBauGuiItem() {
            super(19);
        }

        @Override
        public ItemStack getItem(Player player) {
            return new SWItem(Material.CLOCK, BauSystem.MESSAGE.parse("TPSLIMIT_GUI_ITEM_NAME", player), Arrays.asList(BauSystem.MESSAGE.parse("TPSLIMIT_GUI_ITEM_LORE", player, TickManager.impl.getTickRate())), false, clickType -> {
            }).getItemStack();
        }

        @Override
        public boolean click(ClickType click, Player p) {
            p.closeInventory();
            SWAnvilInv inv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("TPSLIMIT_ANVIL_GUI", p));
            inv.setItem(Material.CLOCK);
            inv.setCallback(s -> p.performCommand("tpslimit " + s));
            inv.open();
            return false;
        }

        @Override
        public Permission permission() {
            return Permission.BUILD;
        }
    }
}
