package com.tiedup.remake.events.system;

import com.tiedup.remake.blocks.BlockCellCore;
import com.tiedup.remake.blocks.entity.CellCoreBlockEntity;
import com.tiedup.remake.cells.*;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ForgeRegistries;

/**
 * Central event handler for Cell System V2.
 *
 * Handles:
 * - Selection mode (block click capture for Set Spawn/Delivery/Disguise)
 * - Door control (block prisoners/non-owners from cell doors)
 * - Breach detection (wall break → BREACHED/COMPROMISED state)
 * - Breach repair (block placed at breached position → repair)
 * - Selection timeout/cancel (player tick)
 */
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class CellV2EventHandler {

    // ==================== RIGHT-CLICK BLOCK ====================

    @SubscribeEvent(priority = EventPriority.HIGH)
    public static void onRightClickBlock(
        PlayerInteractEvent.RightClickBlock event
    ) {
        if (event.getLevel().isClientSide()) return;
        if (!(event.getEntity() instanceof ServerPlayer player)) return;

        UUID playerId = player.getUUID();
        BlockPos clickedPos = event.getPos();
        ServerLevel level = player.serverLevel();

        // 1. Check selection mode first
        CellSelectionManager.SelectionContext selection =
            CellSelectionManager.getSelection(playerId);
        if (selection != null) {
            event.setCanceled(true);
            handleSelectionClick(player, level, clickedPos, selection);
            return;
        }

        // 2. Check door control
        handleDoorControl(event, player, level, clickedPos);
    }

    // ==================== BLOCK BREAK ====================

    @SubscribeEvent(priority = EventPriority.HIGH)
    public static void onBlockBreak(BlockEvent.BreakEvent event) {
        if (!(event.getLevel() instanceof ServerLevel level)) return;

        BlockPos pos = event.getPos();
        Player breaker = event.getPlayer();
        CellRegistryV2 registry = CellRegistryV2.get(level);

        // Check if broken block is a wall of a V2 cell
        UUID cellId = registry.getCellIdAtWall(pos);
        if (cellId == null) return;

        CellDataV2 cell = registry.getCell(cellId);
        if (cell == null) return;

        // Prisoners cannot break the Cell Core
        if (level.getBlockState(pos).getBlock() instanceof BlockCellCore) {
            if (cell.hasPrisoner(breaker.getUUID())) {
                event.setCanceled(true);
                breaker.displayClientMessage(
                    Component.translatable(
                        "msg.tiedup.cell_core.cant_break_core"
                    ).withStyle(ChatFormatting.RED),
                    true
                );
                return;
            }
        }

        // Record breach
        registry.addBreach(cellId, pos);

        // State transitions
        float breachPct = cell.getBreachPercentage();
        if (breachPct > 0.30f && cell.getState() != CellState.COMPROMISED) {
            cell.setState(CellState.COMPROMISED);
            TiedUpMod.LOGGER.info(
                "[CellV2EventHandler] Cell {} COMPROMISED ({}% breached)",
                cellId.toString().substring(0, 8),
                (int) (breachPct * 100)
            );
        } else if (cell.getState() == CellState.INTACT) {
            cell.setState(CellState.BREACHED);
            TiedUpMod.LOGGER.info(
                "[CellV2EventHandler] Cell {} BREACHED at {}",
                cellId.toString().substring(0, 8),
                pos.toShortString()
            );
        }

        // Notify owner (skip if breaker is the owner)
        if (cell.getOwnerId() != null && !cell.isOwnedBy(breaker.getUUID())) {
            ServerPlayer owner = level
                .getServer()
                .getPlayerList()
                .getPlayer(cell.getOwnerId());
            if (owner != null) {
                String cellName =
                    cell.getName() != null
                        ? cell.getName()
                        : "Cell " + cellId.toString().substring(0, 8);
                SystemMessageManager.sendToPlayer(
                    owner,
                    SystemMessageManager.MessageCategory.CELL_BREACH
                );
            }
        }
    }

    // ==================== BLOCK PLACE ====================

    @SubscribeEvent
    public static void onBlockPlace(BlockEvent.EntityPlaceEvent event) {
        if (!(event.getLevel() instanceof ServerLevel level)) return;

        BlockPos pos = event.getPos();
        CellRegistryV2 registry = CellRegistryV2.get(level);

        // Check if placed position is a breached wall
        UUID cellId = registry.getCellIdAtBreach(pos);
        if (cellId == null) return;

        CellDataV2 cell = registry.getCell(cellId);
        if (cell == null) return;

        // Only repair if the placed block is solid
        BlockState placedState = event.getPlacedBlock();
        if (!placedState.isSolid()) return;

        registry.repairBreach(cellId, pos);

        TiedUpMod.LOGGER.debug(
            "[CellV2EventHandler] Breach repaired at {} in cell {}",
            pos.toShortString(),
            cellId.toString().substring(0, 8)
        );

        // Check if all breaches are repaired
        if (
            cell.getBreachedPositions().isEmpty() &&
            cell.getState() == CellState.BREACHED
        ) {
            cell.setState(CellState.INTACT);

            // Notify owner
            if (cell.getOwnerId() != null) {
                ServerPlayer owner = level
                    .getServer()
                    .getPlayerList()
                    .getPlayer(cell.getOwnerId());
                if (owner != null) {
                    owner.displayClientMessage(
                        Component.translatable(
                            "msg.tiedup.cell_core.breach_repaired"
                        ).withStyle(ChatFormatting.GREEN),
                        true
                    );
                }
            }

            TiedUpMod.LOGGER.info(
                "[CellV2EventHandler] Cell {} fully repaired → INTACT",
                cellId.toString().substring(0, 8)
            );
        }
    }

    // ==================== PLAYER TICK ====================

    @SubscribeEvent
    public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
        if (event.phase != TickEvent.Phase.END) return;
        if (!(event.player instanceof ServerPlayer player)) return;

        UUID playerId = player.getUUID();
        if (!CellSelectionManager.isInSelectionMode(playerId)) return;

        // Cancel on sneak
        if (player.isShiftKeyDown()) {
            CellSelectionManager.clearSelection(playerId);
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.selection.cancelled"
                ).withStyle(ChatFormatting.YELLOW),
                true
            );
            return;
        }

        // Cancel on timeout or distance
        if (
            CellSelectionManager.shouldCancel(playerId, player.blockPosition())
        ) {
            CellSelectionManager.clearSelection(playerId);
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.selection.cancelled"
                ).withStyle(ChatFormatting.YELLOW),
                true
            );
        }
    }

    // ==================== SELECTION HANDLING ====================

    private static void handleSelectionClick(
        ServerPlayer player,
        ServerLevel level,
        BlockPos clickedPos,
        CellSelectionManager.SelectionContext selection
    ) {
        CellRegistryV2 registry = CellRegistryV2.get(level);
        CellDataV2 cell = registry.getCell(selection.cellId);
        if (cell == null) {
            CellSelectionManager.clearSelection(player.getUUID());
            return;
        }

        switch (selection.mode) {
            case SET_SPAWN -> handleSetSpawn(
                player,
                level,
                clickedPos,
                cell,
                selection
            );
            case SET_DELIVERY -> handleSetDelivery(
                player,
                level,
                clickedPos,
                cell,
                selection
            );
            case SET_DISGUISE -> handleSetDisguise(
                player,
                level,
                clickedPos,
                cell,
                selection
            );
        }
    }

    private static void handleSetSpawn(
        ServerPlayer player,
        ServerLevel level,
        BlockPos clickedPos,
        CellDataV2 cell,
        CellSelectionManager.SelectionContext selection
    ) {
        // Spawn must be inside the cell or on a wall block (e.g. floor)
        boolean isInterior = cell.isContainedInCell(clickedPos);
        boolean isWall = cell.isWallBlock(clickedPos);

        if (!isInterior && !isWall) {
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.not_inside_cell"
                ).withStyle(ChatFormatting.RED),
                true
            );
            return;
        }

        // If clicked on a wall (floor/ceiling/wall), spawn above it
        BlockPos actualSpawn = isWall ? clickedPos.above() : clickedPos;

        // Verify the actual spawn position is inside the cell
        if (isWall && !cell.isContainedInCell(actualSpawn)) {
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.not_inside_cell"
                ).withStyle(ChatFormatting.RED),
                true
            );
            return;
        }

        // Update spawn in both Core BE and CellDataV2
        BlockEntity be = level.getBlockEntity(selection.corePos);
        if (be instanceof CellCoreBlockEntity core) {
            core.setSpawnPoint(actualSpawn);
        }
        cell.setSpawnPoint(actualSpawn);

        CellSelectionManager.clearSelection(player.getUUID());
        player.displayClientMessage(
            Component.translatable("msg.tiedup.cell_core.spawn_set").withStyle(
                ChatFormatting.GREEN
            ),
            true
        );
    }

    private static void handleSetDelivery(
        ServerPlayer player,
        ServerLevel level,
        BlockPos clickedPos,
        CellDataV2 cell,
        CellSelectionManager.SelectionContext selection
    ) {
        // Delivery must be outside the cell
        if (cell.isContainedInCell(clickedPos)) {
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.must_be_outside"
                ).withStyle(ChatFormatting.RED),
                true
            );
            return;
        }

        BlockEntity be = level.getBlockEntity(selection.corePos);
        if (be instanceof CellCoreBlockEntity core) {
            core.setDeliveryPoint(clickedPos);
        }
        cell.setDeliveryPoint(clickedPos);

        CellSelectionManager.clearSelection(player.getUUID());
        player.displayClientMessage(
            Component.translatable(
                "msg.tiedup.cell_core.delivery_set"
            ).withStyle(ChatFormatting.GREEN),
            true
        );
    }

    private static void handleSetDisguise(
        ServerPlayer player,
        ServerLevel level,
        BlockPos clickedPos,
        CellDataV2 cell,
        CellSelectionManager.SelectionContext selection
    ) {
        BlockState clickedState = level.getBlockState(clickedPos);

        // Must be a solid block
        if (!clickedState.isSolid()) {
            player.displayClientMessage(
                Component.translatable(
                    "msg.tiedup.cell_core.must_be_solid"
                ).withStyle(ChatFormatting.RED),
                true
            );
            return;
        }

        BlockEntity be = level.getBlockEntity(selection.corePos);
        if (be instanceof CellCoreBlockEntity core) {
            core.setDisguiseState(clickedState);
        }

        CellSelectionManager.clearSelection(player.getUUID());
        String blockName = clickedState.getBlock().getName().getString();
        player.displayClientMessage(
            Component.translatable(
                "msg.tiedup.cell_core.disguise_set",
                blockName
            ).withStyle(ChatFormatting.GREEN),
            true
        );
    }

    // ==================== DOOR CONTROL ====================

    private static void handleDoorControl(
        PlayerInteractEvent.RightClickBlock event,
        ServerPlayer player,
        ServerLevel level,
        BlockPos clickedPos
    ) {
        CellRegistryV2 registry = CellRegistryV2.get(level);

        // Check if the clicked position is a cell door (doors are in wall set)
        // or an interior linked redstone (buttons/levers)
        CellDataV2 cellByWall = registry.getCellByWall(clickedPos);
        CellDataV2 cellByInterior = registry.getCellContaining(clickedPos);

        CellDataV2 cell = null;
        boolean isDoor = false;
        boolean isRedstone = false;

        if (cellByWall != null) {
            // Check if it's in the cell's doors list
            if (cellByWall.getDoors().contains(clickedPos)) {
                cell = cellByWall;
                isDoor = true;
            }
            // Check linked redstone in walls
            if (cellByWall.getLinkedRedstone().contains(clickedPos)) {
                cell = cellByWall;
                isRedstone = true;
            }
        }

        if (cellByInterior != null && !isDoor && !isRedstone) {
            // Check linked redstone in interior
            if (cellByInterior.getLinkedRedstone().contains(clickedPos)) {
                cell = cellByInterior;
                isRedstone = true;
            }
        }

        if (cell == null || (!isDoor && !isRedstone)) return;

        // Owner, camp cell, or OP can always interact
        if (cell.canPlayerManage(player.getUUID(), player.hasPermissions(2))) {
            return;
        }

        // Prisoner or non-owner → block interaction
        event.setCanceled(true);
        player.displayClientMessage(
            Component.translatable(
                "msg.tiedup.cell_core.door_locked"
            ).withStyle(ChatFormatting.RED),
            true
        );
    }
}
