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

import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.script.ScriptRunner;
import de.steamwar.bausystem.features.script.lua.SteamWarGlobalLuaPlugin;
import de.steamwar.bausystem.features.script.lua.libs.StorageLib;
import de.steamwar.bausystem.region.Region;
import de.steamwar.core.SWPlayer;
import de.steamwar.linkage.Linked;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
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.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.player.*;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;

@Linked
public class EventListener implements Listener {

    static {
        Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
            long millis = System.currentTimeMillis();
            SWPlayer.allWithSingleComponent(ScriptRunner.ScriptData.class)
                    .filter(pair -> millis - pair.getComponent().getLastF() > 200)
                    .forEach(pair -> pair.getComponent().setLastF(Long.MAX_VALUE));
        }, 1, 1);
    }

    @EventHandler(priority = EventPriority.MONITOR)
    public void onPlayerJoin(PlayerJoinEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.SelfJoin, LuaValue.NIL, event);
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onPlayerQuit(PlayerQuitEvent event) {
        if (Permission.BUILD.hasPermission(event.getPlayer())) {
            ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.SelfLeave, LuaValue.NIL, event);
        }
        StorageLib.removePlayer(event.getPlayer());
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new);
        if (scriptData.getLastF() != Long.MAX_VALUE) {
            Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
                ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.DoubleSwap, LuaValue.NIL, event);
            }, 1);
        } else {
            scriptData.setLastF(System.currentTimeMillis());
        }
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onBlockPlace(BlockPlaceEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        LuaTable table = new LuaTable();
        table.set("x", event.getBlock().getX());
        table.set("y", event.getBlock().getY());
        table.set("z", event.getBlock().getZ());
        table.set("type", event.getBlock().getType().name());
        ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.PlaceBlock, table, event);
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onBlockBreak(BlockBreakEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        LuaTable table = new LuaTable();
        table.set("x", event.getBlock().getX());
        table.set("y", event.getBlock().getY());
        table.set("z", event.getBlock().getZ());
        table.set("type", event.getBlock().getType().name());
        ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.BreakBlock, table, event);
    }

    @EventHandler(priority = EventPriority.LOW)
    public void onPlayerInteract(PlayerInteractEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new);
        if (scriptData.isIgnore()) {
            scriptData.setIgnore(false);
            return;
        }
        LuaTable table = new LuaTable();
        table.set("action", event.getAction().name());
        if (event.getHand() == null) {
            table.set("hand", "null");
        } else {
            table.set("hand", event.getHand().name());
        }
        table.set("block", event.getItem() == null ? Material.AIR.name() : event.getItem().getType().name());
        if (event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_BLOCK) {
            table.set("hasBlock", LuaValue.valueOf(true));
            table.set("blockX", event.getClickedBlock().getX());
            table.set("blockY", event.getClickedBlock().getY());
            table.set("blockZ", event.getClickedBlock().getZ());
            table.set("blockFace", event.getBlockFace().name());
            table.set("blockType", event.getClickedBlock().getType().name());
        } else {
            table.set("hasBlock", LuaValue.valueOf(false));
        }

        if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
            ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.RightClick, table, event);
        } else if (event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) {
            ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.LeftClick, table, event);
        }
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onEntitySpawn(EntitySpawnEvent event) {
        if (event.getEntityType() != EntityType.TNT) {
            return;
        }
        Region tntRegion = Region.getRegion(event.getLocation());

        for (Player player : Bukkit.getOnlinePlayers()) {
            if (!Permission.BUILD.hasPermission(player)) continue;
            if (tntRegion.getArea().inRegion(player.getLocation(), false)) {
                ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.TNTSpawn, LuaValue.NIL, event);
            }
        }
    }

    @EventHandler(priority = EventPriority.LOWEST)
    public void onEntityExplode(EntityExplodeEvent event) {
        if (event.getEntityType() != EntityType.TNT) {
            return;
        }
        Region tntRegion = Region.getRegion(event.getLocation());

        LuaTable table = new LuaTable();
        table.set("x", event.getLocation().getX());
        table.set("y", event.getLocation().getY());
        table.set("z", event.getLocation().getZ());

        boolean inBuild = event.blockList().stream().anyMatch(block -> tntRegion.getBuildArea().inRegion(block.getLocation(), true));

        for (Player player : Bukkit.getOnlinePlayers()) {
            if (!Permission.BUILD.hasPermission(player)) continue;
            if (tntRegion.getArea().inRegion(player.getLocation(), false)) {
                ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.TNTExplode, table, event);
                if (inBuild) {
                    ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.TNTExplodeInBuild, table, event);
                }
            }
        }
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onPlayerDropItem(PlayerDropItemEvent event) {
        if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
        ScriptRunner.ScriptData scriptData = SWPlayer.of(event.getPlayer()).getComponentOrDefault(ScriptRunner.ScriptData.class, ScriptRunner.ScriptData::new);
        scriptData.setIgnore(true);
        LuaTable table = new LuaTable();
        table.set("type", event.getItemDrop().getItemStack().getType().name());
        ScriptRunner.callEvent(event.getPlayer(), SteamWarGlobalLuaPlugin.EventType.DropItem, table, event);
    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onEntityDeath(EntityDeathEvent event) {
        for (Player player : Bukkit.getOnlinePlayers()) {
            if (!Permission.BUILD.hasPermission(player)) continue;
            LuaTable table = new LuaTable();
            table.set("type", event.getEntityType().name());
            ScriptRunner.callEvent(player, SteamWarGlobalLuaPlugin.EventType.EntityDeath, table, event);
        }
    }
}
