/*
 *   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.SWUtils;
import de.steamwar.bausystem.features.loader.elements.LoaderElement;
import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
import de.steamwar.bausystem.features.loader.elements.impl.*;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.EquipmentSlot;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

public class LoaderRecorder implements Listener {

    private Player player;
    private List<LoaderElement> loaderElementList;
    private long lastInteraction = TPSUtils.currentRealTick.get();

    public LoaderRecorder(Player player, List<LoaderElement> loaderElementList) {
        this.player = player;
        this.loaderElementList = loaderElementList;
        Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
    }

    public void stop() {
        addWaitTime(true);
        HandlerList.unregisterAll(this);
        player = null;
        blockSet.clear();
    }

    private void addWaitTime(boolean last) {
        if (loaderElementList.isEmpty()) {
            lastInteraction = TPSUtils.currentRealTick.get();
            return;
        }
        if (loaderElementList.get(loaderElementList.size() - 1) instanceof LoaderWait) {
            return;
        }
        long diff = TPSUtils.currentRealTick.get() - lastInteraction;
        if (last) diff = 120;
        lastInteraction = TPSUtils.currentRealTick.get();
        loaderElementList.add(new LoaderWait(diff));
    }

    @EventHandler
    public void onBlockPlace(BlockPlaceEvent event) {
        if (event.getPlayer() != player) return;
        if (event.getBlock().getType() != Material.TNT) return;

        addWaitTime(false);
        loaderElementList.add(new LoaderTNT(event.getBlock().getLocation()));
        message("LOADER_BUTTON_TNT");
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        if (event.getPlayer() != player) return;

        boolean removedOne = false;
        for (int i = 0; i < loaderElementList.size(); i++) {
            LoaderElement element = loaderElementList.get(i);
            if (!(element instanceof LoaderInteractionElement)) continue;
            LoaderInteractionElement interactionElement = (LoaderInteractionElement) element;
            if (interactionElement.getLocation().equals(event.getBlock().getLocation())) {
                loaderElementList.remove(i);
                if (i > 0) {
                    loaderElementList.remove(i - 1);
                }
                removedOne = true;
                break;
            }
        }

        if (removedOne) {
            if (event.getBlock().getType() != Material.TNT) {
                event.setCancelled(true);
            }
            SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("LOADER_MESSAGE_UNINTERACT", player));
        }
    }

    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (event.getPlayer() != player) return;
        if (player.isSneaking()) return;
        if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.PHYSICAL) return;
        if (event.getClickedBlock().getType() == Material.OBSERVER) return;
        if (event.getHand() == EquipmentSlot.OFF_HAND) return;

        addWaitTime(false);
        Block block = event.getClickedBlock();
        getLoaderInteractionElement(block, (loaderInteractionElement, s) -> {
            loaderElementList.add(loaderInteractionElement);
            message(s);
        });
    }

    private Map<Location, Long> blockSet = new HashMap<>();
    private Map<Location, LoaderMovement> movementSet = new HashMap<>();

    @EventHandler
    public void onPlayerMove(PlayerMoveEvent event) {
        if (event.getPlayer() != player) return;

        Block fromBlock = event.getFrom().getBlock();
        Block toBlock = event.getTo().getBlock();
        calcMovementBlocks(fromBlock, toBlock);

        fromBlock = fromBlock.getRelative(0, 1, 0);
        toBlock = toBlock.getRelative(0, 1, 0);
        calcMovementBlocks(fromBlock, toBlock);
    }

    private void calcMovementBlocks(Block fromBlock, Block toBlock) {
        if (!blockSet.containsKey(toBlock.getLocation())) {
            Long startTime = blockSet.remove(fromBlock.getLocation());
            LoaderMovement loaderMovement = movementSet.remove(fromBlock.getLocation());
            if (loaderMovement != null && startTime != null) {
                loaderMovement.setInitialTicks(TPSUtils.currentRealTick.get() - startTime);
            }

            blockSet.put(toBlock.getLocation(), TPSUtils.currentRealTick.get());

            addWaitTime(false);
            loaderMovement = null;
            Material type = toBlock.getType();
            switch (type) {
                case TRIPWIRE:
                    loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_TRIPWIRE", Material.STRING);
                    message("LOADER_BUTTON_TRIPWIRE");
                    break;
                case LIGHT_WEIGHTED_PRESSURE_PLATE:
                case HEAVY_WEIGHTED_PRESSURE_PLATE:
                    loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE", type);
                    message("LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE");
                    break;
                default:
                    if (type.name().endsWith("PRESSURE_PLATE")) {
                        loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_PRESSURE_PLATE", type);
                        message("LOADER_BUTTON_PRESSURE_PLATE");
                    }
                    break;
            }
            if (loaderMovement != null) {
                movementSet.put(toBlock.getLocation(), loaderMovement);
                loaderElementList.add(loaderMovement);
            }
        }
    }

    public static void getLoaderInteractionElement(Block block, BiConsumer<LoaderInteractionElement<?>, String> consumer) {
        Material type = block.getType();
        switch (type) {
            case COMPARATOR:
                consumer.accept(new LoaderComparator(block.getLocation()), "LOADER_BUTTON_COMPARATOR");
                break;
            case REPEATER:
                consumer.accept(new LoaderRepeater(block.getLocation()), "LOADER_BUTTON_REPEATER");
                break;
            case NOTE_BLOCK:
                consumer.accept(new LoaderNoteBlock(block.getLocation()), "LOADER_BUTTON_NOTEBLOCK");
                break;
            case LEVER:
                consumer.accept(new LoaderLever(block.getLocation()), "LOADER_BUTTON_SWITCH");
                break;
            case DAYLIGHT_DETECTOR:
                consumer.accept(new LoaderDaylightDetector(block.getLocation()), "LOADER_BUTTON_DAYLIGHT_DETECTOR");
                break;
            case LECTERN:
                consumer.accept(new LoaderLectern(block.getLocation()), "LOADER_BUTTON_LECTERN");
                break;
            case IRON_TRAPDOOR:
            case IRON_DOOR:
                break;
            default:
                if (type.name().endsWith("_TRAPDOOR")) {
                    consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_TRAPDOOR", type), "LOADER_BUTTON_TRAPDOOR");
                } else if (type.name().endsWith("_DOOR")) {
                    consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_DOOR", type), "LOADER_BUTTON_DOOR");
                } else if (type.name().endsWith("FENCE_GATE")) {
                    consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_FENCEGATE", type), "LOADER_BUTTON_FENCEGATE");
                } else if (type.name().endsWith("STONE_BUTTON")) {
                    consumer.accept(new LoaderTicks(block.getLocation(), "LOADER_BUTTON_STONE_BUTTON", type, 20), "LOADER_BUTTON_STONE_BUTTON");
                } else if (type.name().endsWith("BUTTON")) {
                    consumer.accept(new LoaderTicks(block.getLocation(), "LOADER_BUTTON_WOOD_BUTTON", type, 30), "LOADER_BUTTON_WOOD_BUTTON");
                }
                break;
        }
    }

    private void message(String type) {
        SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("LOADER_MESSAGE_INTERACT", player, BauSystem.MESSAGE.parse(type, player), loaderElementList.size()));
    }
}
