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

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.loader.elements.LoaderElement;
import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
import de.steamwar.bausystem.features.loader.elements.impl.LoaderTNT;
import de.steamwar.bausystem.features.loader.elements.impl.LoaderWait;
import de.steamwar.bausystem.shared.EnumDisplay;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.inventory.SWItem;
import de.steamwar.inventory.SWListInv;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public class Loader implements Listener {

    private static final Map<Player, Loader> LOADER_MAP = new HashMap<>();

    public static Loader getLoader(Player player) {
        return LOADER_MAP.get(player);
    }

    public static void newLoader(Player player) {
        LOADER_MAP.put(player, new Loader(player));
    }

    private final Player p;

    @Getter
    private Stage stage = Stage.SETUP;
    private LoaderRecorder recorder;

    private List<LoaderElement> elements = new ArrayList<>();
    private int currentElement = 0;
    private long waitTime = 0;

    public Loader(Player p) {
        this.p = p;
        this.recorder = new LoaderRecorder(p, elements);
        Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());

        BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
            if (stage != Stage.RUNNING && stage != Stage.SINGLE) return;
            if(!Permission.BUILD.hasPermission(p)) return;
            if (waitTime > 0) {
                waitTime--;
                return;
            }
            if (currentElement >= elements.size()) {
                currentElement = 0;
                if (stage == Stage.SINGLE) {
                    stage = Stage.PAUSE;
                    return;
                }
            }

            while (currentElement < elements.size()) {
                LoaderElement element = elements.get(currentElement);
                currentElement++;
                element.execute(delay -> waitTime = delay);
                if (waitTime > 0) {
                    if (element instanceof LoaderTNT) currentElement--;
                    waitTime--;
                    return;
                }
            }
        }, 0, 1);
    }

    public void single() {
        if (stage == Stage.END) return;
        if (stage == Stage.RUNNING) return;
        stage = Stage.SINGLE;
        if (recorder != null) {
            recorder.stop();
            recorder = null;
        }
        if (elements.isEmpty()) {
            BauSystem.MESSAGE.send("LOADER_NOTHING_RECORDED", p);
            stop();
        }
    }

    public void start() {
        if (stage == Stage.END) return;
        if (stage == Stage.RUNNING) return;
        stage = Stage.RUNNING;
        if (recorder != null) {
            recorder.stop();
            recorder = null;
        }
        if (elements.isEmpty()) {
            BauSystem.MESSAGE.send("LOADER_NOTHING_RECORDED", p);
            stop();
        }
    }

    public void pause() {
        if (stage == Stage.END) return;
        if (stage == Stage.PAUSE) return;
        stage = Stage.PAUSE;
        if (recorder != null) {
            recorder.stop();
            recorder = null;
        }
    }

    public void stop() {
        stage = Stage.END;
        if (recorder != null) {
            recorder.stop();
            recorder = null;
        }
        elements.clear();
        LOADER_MAP.remove(p);
    }

    public boolean setTicksBetweenShots(int delay) {
        if (elements.isEmpty()) return false;
        LoaderElement loaderElement = elements.get(elements.size() - 1);
        if (loaderElement instanceof LoaderWait) {
            ((LoaderWait) loaderElement).setDelay(delay);
        } else {
            LoaderWait loaderWait = new LoaderWait(delay);
            elements.add(loaderWait);
            pause();
        }
        return true;
    }

    public void setTicksBetweenBlocks(int delay) {
        for (int i = 0; i < elements.size() - 1; i++) {
            LoaderElement loaderElement = elements.get(i);
            if (loaderElement instanceof LoaderWait) {
                ((LoaderWait) loaderElement).setDelay(delay);
            }
        }
    }

    public void gui(SettingsSorting settingsSorting) {
        List<SWListInv.SWListEntry<LoaderElement>> list = new ArrayList<>();
        AtomicBoolean allWait = new AtomicBoolean(true);
        Runnable updateRunnable = () -> {
            list.clear();
            for (int i = 0; i < elements.size(); i++) {
                LoaderElement previous = i > 0 ? elements.get(i - 1) : null;
                LoaderElement current = elements.get(i);
                LoaderElement next = i < elements.size() - 1 ? elements.get(i + 1) : null;

                if (!settingsSorting.shouldShow(previous, current, next)) {
                    continue;
                }

                if ((!(current instanceof LoaderWait))) {
                    allWait.set(false);
                }

                SWItem item = current.menu(p);
                if (current instanceof LoaderInteractionElement<?>) {
                    LoaderInteractionElement<?> interactionElement = (LoaderInteractionElement<?>) current;
                    List<String> lore = new ArrayList<>();
                    if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) {
                        lore.addAll(item.getItemMeta().getLore());
                        lore.add("§8");
                    }
                    lore.add(BauSystem.MESSAGE.parse("LOADER_SETTING_MODES", p, interactionElement.size()));
                    lore.add(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", p));
                    item.setLore(lore);
                } else {
                    List<String> lore = new ArrayList<>();
                    if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) {
                        lore.addAll(item.getItemMeta().getLore());
                        lore.add("§8");
                    }
                    lore.add(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", p));
                    item.setLore(lore);
                }
                list.add(new SWListInv.SWListEntry<>(item, current));
            }
            if (list.isEmpty()) {
                allWait.set(false);
            }
        };
        updateRunnable.run();

        SWListInv<LoaderElement> swListInv = new SWListInv<>(p, BauSystem.MESSAGE.parse("LOADER_GUI_TITLE", p), false, list, (clickType, loaderElement) -> {});
        swListInv.setCallback((clickType, entry) -> entry.click(p, () -> {
            updateRunnable.run();
            swListInv.open();
        }));

        SWItem settingItem = new SWItem(settingsSorting.getMaterial(), "§e" + BauSystem.MESSAGE.parse(settingsSorting.getName(), p), clickType -> {
            if (clickType == ClickType.LEFT) {
                int index = settingsSorting.ordinal() + 1;
                if (index >= SettingsSorting.LENGTH) {
                    index = 0;
                }
                gui(SettingsSorting.values()[index]);
            } else if (clickType == ClickType.RIGHT) {
                int index = settingsSorting.ordinal() - 1;
                if (index < 0) {
                    index = SettingsSorting.LENGTH - 1;
                }
                gui(SettingsSorting.values()[index]);
            }
        });
        List<String> strings = new ArrayList<>();
        for (SettingsSorting setting : SettingsSorting.values()) {
            if (setting == settingsSorting) {
                strings.add("§e> §7" + BauSystem.MESSAGE.parse(setting.getName(), p));
            } else {
                strings.add("§8> §7" + BauSystem.MESSAGE.parse(setting.getName(), p));
            }
        }
        settingItem.setLore(strings);
        swListInv.setItem(48, settingItem);

        if (allWait.get()) {
            SWItem setWait = new SWItem(Material.PAPER, BauSystem.MESSAGE.parse("LOADER_GUI_SHOW_WAITS_SET_ALL", p), clickType -> {
                SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("LOADER_GUI_SHOW_WAITS_TITLE", p), "");
                swAnvilInv.setCallback(s -> {
                    try {
                        long delay = Math.max(Long.parseLong(s), 0);
                        list.forEach(loaderElementSWListEntry -> {
                            ((LoaderWait) loaderElementSWListEntry.getObject()).setDelay(delay);
                        });
                    } catch (NumberFormatException ignored) {
                    }
                    gui(settingsSorting);
                });
                swAnvilInv.open();
            });
            swListInv.setItem(50, setWait);
        } else {
            swListInv.setItem(50, new SWItem(Material.GRAY_STAINED_GLASS_PANE, "§8"));
        }
        swListInv.open();
    }

    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        if (event.getPlayer() != p) return;
        stop();
    }

    public String getProgress() {
        return Math.max(currentElement, 1) + "§8/§7" + elements.size();
    }

    public enum SettingsSorting {
        ALL {
            @Override
            public Material getMaterial() {
                return Material.STRUCTURE_VOID;
            }

            @Override
            public String getName() {
                return "LOADER_GUI_SHOW_ALL";
            }

            @Override
            public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
                return true;
            }
        },
        WAIT {
            @Override
            public Material getMaterial() {
                return Material.CLOCK;
            }

            @Override
            public String getName() {
                return "LOADER_GUI_SHOW_WAITS";
            }

            @Override
            public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
                return current instanceof LoaderWait;
            }
        },
        WAIT_BETWEEN_TNT {
            @Override
            public Material getMaterial() {
                return Material.REDSTONE_BLOCK;
            }

            @Override
            public String getName() {
                return "LOADER_GUI_SHOW_WAITS_BETWEEN_TNT";
            }

            @Override
            public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
                return previous instanceof LoaderTNT && current instanceof LoaderWait && next instanceof LoaderTNT;
            }
        },
        INTERACTIONS {
            @Override
            public Material getMaterial() {
                return Material.REPEATER;
            }

            @Override
            public String getName() {
                return "LOADER_GUI_SHOW_INTERACTIONS";
            }

            @Override
            public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
                return current instanceof LoaderInteractionElement && !(current instanceof LoaderTNT);
            }
        },
        TNT {
            @Override
            public Material getMaterial() {
                return Material.TNT;
            }

            @Override
            public String getName() {
                return "LOADER_GUI_SHOW_TNT";
            }

            @Override
            public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
                return current instanceof LoaderTNT;
            }
        },
        ;

        public static int LENGTH = SettingsSorting.values().length;

        public abstract Material getMaterial();
        public abstract String getName();
        public abstract boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next);
    }

    @AllArgsConstructor
    public enum Stage implements EnumDisplay {
        SETUP("LOADER_SETUP"),
        RUNNING("LOADER_RUNNING"),
        SINGLE("LOADER_SINGLE"),
        PAUSE("LOADER_PAUSE"),
        END("LOADER_END");

        @Getter
        private final String chatValue;
    }
}
