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

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.bausystem.features.autostart.AutostartListener;
import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
import de.steamwar.core.SWPlayer;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.*;
import org.bukkit.block.data.type.DaylightDetector;
import org.bukkit.block.data.type.Switch;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;

import java.util.*;

@UtilityClass
public class Detonator {

    public class DetonatorComponent implements SWPlayer.Component {
        private final REntityServer entities = new REntityServer();

        @Getter
        @Setter
        private boolean hasUpdated = false;

        @Override
        public void onMount(SWPlayer player) {
            entities.addPlayer(player.getPlayer());
            entities.setCallback((player1, entity, action) -> {
                Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ());
                DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player1);
            });
        }

        @Override
        public void onUnmount(SWPlayer player) {
            entities.close();
        }
    }

    private static final Vector HALF = new Vector(0.5, 0, 0.5);

    public static boolean isDetonator(ItemStack itemStack) {
        return ItemStorage.isDetonator(itemStack);
    }

    public static void showDetonator(Player p, List<Location> locs) {
        DetonatorComponent detonatorComponent = SWPlayer.of(p).getComponentOrDefault(DetonatorComponent.class, DetonatorComponent::new);
        locs.forEach(location -> {
            RFallingBlockEntity entity = new RFallingBlockEntity(detonatorComponent.entities, location.clone().add(HALF), Material.RED_STAINED_GLASS);
            entity.setNoGravity(true);
        });
    }

    public static void hideDetonator(Player p) {
        SWPlayer.of(p).removeComponent(DetonatorComponent.class);
    }

    public static boolean hasActiveDetonatorShow(Player p) {
        return SWPlayer.of(p).hasComponent(DetonatorComponent.class);
    }

    public static void activateDetonator(DetonatorStorage detonator) {
        Player p = detonator.getPlayer();
        if (Config.getInstance().get(p).getPlainValueOrDefault("detonator-autostart", false)) {
            AutostartListener.instance.activate(p);
        }

        Map<Integer, Set<Block>> deactivate = new HashMap<>();
        Set<Location> invalid = new HashSet<>();

        for (Location location : detonator.getLocations()) {
            Block block = location.getBlock();
            Detoblock detoblock = getBlock(block);

            if (detoblock == Detoblock.INVALID) {
                invalid.add(location);
                continue;
            }

            if (detoblock.isToggle()) {
                setBlockPower(block, !getBlockPower(block));
                updateButton(block, detoblock);
            } else {
                setBlockPower(block, true);
                updateButton(block, detoblock);
                deactivate.computeIfAbsent(detoblock.getTime(), integer -> new HashSet<>()).add(block);
            }
        }

        int s = detonator.getLocations().size() - invalid.size();
        if (s == 1) {
            SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_POINT_ACT", p));
        } else {
            SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_POINTS_ACT", p, s));
        }

        if (!invalid.isEmpty()) {
            int invalidPoints = invalid.size();
            if (invalidPoints == 1) {
                BauSystem.MESSAGE.send("DETONATOR_INVALID_POINT", p);
            } else {
                BauSystem.MESSAGE.send("DETONATOR_INVALID_POINTS", p, invalidPoints);
            }
            detonator.write();
        }

        deactivate.forEach((integer, blocks1) ->
                Bukkit.getScheduler().scheduleSyncDelayedTask(BauSystem.getInstance(), () -> blocks1.forEach(block -> {
                    setBlockPower(block, false);
                    updateButton(block, getBlock(block));
                }), integer));
    }

    public static void updateButton(Block block, Detoblock detoblock) {
        if (block.getBlockData() instanceof Switch) {
            Switch sw = (Switch) block.getBlockData();
            FaceAttachable.AttachedFace face = sw.getAttachedFace();
            if (face == FaceAttachable.AttachedFace.FLOOR) {
                update(block.getRelative(BlockFace.DOWN));
            } else if (face == FaceAttachable.AttachedFace.CEILING) {
                update(block.getRelative(BlockFace.UP));
            } else {
                update(block.getRelative(sw.getFacing().getOppositeFace()));
            }
        } else if (detoblock == Detoblock.TRIPWIRE) {
            update(block);
        } else if (detoblock == Detoblock.PRESSURE_PLATE || detoblock == Detoblock.WEIGHTED_PRESSURE_PLATE) {
            update(block.getRelative(BlockFace.DOWN));
        }
    }

    private static void update(Block block) {
        BlockData data = block.getBlockData();
        block.setType(Material.BARRIER, true);
        block.setBlockData(data, true);
    }

    private static void setBlockPower(Block block, boolean state) {
        BlockData data = block.getBlockData();
        if (data instanceof Powerable) {
            Powerable pow = (Powerable) data;
            pow.setPowered(state);
        }
        if (data instanceof Openable) {
            Openable openable = (Openable) data;
            openable.setOpen(state);
        }
        if (data instanceof DaylightDetector) {
            DaylightDetector detector = (DaylightDetector) data;
            detector.setInverted(state);
        }
        if (data instanceof AnaloguePowerable) {
            AnaloguePowerable powerable = (AnaloguePowerable) data;
            if (block.getType() == Material.REDSTONE_WIRE) {
                powerable.setPower(state ? 15 : 0);
            } else {
                powerable.setPower(state ? 1 : 0);
            }
        }
        block.setBlockData(data);
    }

    private static boolean getBlockPower(Block block) {
        BlockData data = block.getBlockData();
        if (data instanceof Powerable) {
            Powerable pow = (Powerable) data;
            return pow.isPowered();
        }
        if (data instanceof DaylightDetector) {
            DaylightDetector detector = (DaylightDetector) data;
            return detector.isInverted();
        }
        if (data instanceof AnaloguePowerable) {
            AnaloguePowerable powerable = (AnaloguePowerable) data;
            return powerable.getPower() > 0;
        }
        return false;
    }

    static Detoblock getBlock(Block block) {
        switch (block.getType()) {
            case LEVER:
                return Detoblock.SWITCH;
            case TRIPWIRE:
                return Detoblock.TRIPWIRE;
            case NOTE_BLOCK:
                return Detoblock.NOTEBLOCK;
            case DAYLIGHT_DETECTOR:
                return Detoblock.DAYLIGHTSENSOR;
            case HEAVY_WEIGHTED_PRESSURE_PLATE:
            case LIGHT_WEIGHTED_PRESSURE_PLATE:
                return Detoblock.WEIGHTED_PRESSURE_PLATE;
            default:
                if (block.getType().name().contains("STONE_BUTTON")) {
                    return Detoblock.STONE_BUTTON;
                } else if (block.getType().name().contains("BUTTON")) {
                    return Detoblock.WOOD_BUTTON;
                } else if (block.getType().name().contains("PRESSURE_PLATE")) {
                    return Detoblock.PRESSURE_PLATE;
                } else if (block.getBlockData() instanceof Powerable) {
                    return Detoblock.POWERABLE;
                } else {
                    return Detoblock.INVALID;
                }
        }
    }
}
