package com.tiedup.remake.items;

import com.tiedup.remake.blocks.BlockCellCore;
import com.tiedup.remake.blocks.BlockMarker;
import com.tiedup.remake.blocks.ModBlocks;
import com.tiedup.remake.blocks.entity.CellCoreBlockEntity;
import com.tiedup.remake.blocks.entity.MarkerBlockEntity;
import com.tiedup.remake.cells.*;
import com.tiedup.remake.core.SystemMessageManager;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;

/**
 * Admin Wand - Structure marker placement and Cell Core management.
 *
 * Features:
 * - Right-click CellCore: rescan cell (flood-fill)
 * - Shift+Right-click CellCore: display cell info
 * - Right-click elsewhere: cycle structure marker type
 * - Left-click: place/remove/info structure markers
 */
public class ItemAdminWand extends Item {

    private static final String TAG_ACTIVE_CELL_ID = "ActiveCellId";
    private static final String TAG_CURRENT_TYPE = "CurrentType";
    private static final String TAG_WAYPOINT_MODE = "WaypointMode";

    public ItemAdminWand() {
        super(new Item.Properties().stacksTo(1));
    }

    // ==================== NBT ACCESS ====================

    @Nullable
    public static UUID getActiveCellId(ItemStack stack) {
        if (!stack.hasTag() || !stack.getTag().contains(TAG_ACTIVE_CELL_ID)) {
            return null;
        }
        return stack.getTag().getUUID(TAG_ACTIVE_CELL_ID);
    }

    public static void setActiveCellId(ItemStack stack, @Nullable UUID cellId) {
        if (cellId != null) {
            stack.getOrCreateTag().putUUID(TAG_ACTIVE_CELL_ID, cellId);
        } else if (stack.hasTag()) {
            stack.getTag().remove(TAG_ACTIVE_CELL_ID);
        }
    }

    public static MarkerType getCurrentType(ItemStack stack) {
        if (!stack.hasTag() || !stack.getTag().contains(TAG_CURRENT_TYPE)) {
            return MarkerType.ENTRANCE; // Default to structure marker
        }
        return MarkerType.fromString(
            stack.getTag().getString(TAG_CURRENT_TYPE)
        );
    }

    public static void setCurrentType(ItemStack stack, MarkerType type) {
        stack
            .getOrCreateTag()
            .putString(TAG_CURRENT_TYPE, type.getSerializedName());
    }

    // ==================== WAYPOINT MODE ====================

    public static boolean isInWaypointMode(ItemStack stack) {
        return stack.hasTag() && stack.getTag().getBoolean(TAG_WAYPOINT_MODE);
    }

    private static void enterWaypointMode(ItemStack stack, UUID cellId) {
        stack.getOrCreateTag().putBoolean(TAG_WAYPOINT_MODE, true);
        setActiveCellId(stack, cellId);
        // Clear any previous waypoints
        stack.getTag().remove("Waypoints");
    }

    private static void exitWaypointMode(ItemStack stack) {
        if (stack.hasTag()) {
            stack.getTag().remove(TAG_WAYPOINT_MODE);
            stack.getTag().remove("Waypoints");
            setActiveCellId(stack, null);
        }
    }

    private static void addWaypointToStack(ItemStack stack, BlockPos pos) {
        CompoundTag tag = stack.getOrCreateTag();
        ListTag list = tag.contains("Waypoints")
            ? tag.getList("Waypoints", Tag.TAG_COMPOUND)
            : new ListTag();
        CompoundTag wp = new CompoundTag();
        wp.putInt("X", pos.getX());
        wp.putInt("Y", pos.getY());
        wp.putInt("Z", pos.getZ());
        list.add(wp);
        tag.put("Waypoints", list);
    }

    private static List<BlockPos> getWaypointsFromStack(ItemStack stack) {
        List<BlockPos> result = new ArrayList<>();
        if (
            !stack.hasTag() || !stack.getTag().contains("Waypoints")
        ) return result;
        ListTag list = stack.getTag().getList("Waypoints", Tag.TAG_COMPOUND);
        for (int i = 0; i < list.size(); i++) {
            CompoundTag wp = list.getCompound(i);
            result.add(
                new BlockPos(wp.getInt("X"), wp.getInt("Y"), wp.getInt("Z"))
            );
        }
        return result;
    }

    private static void removeLastWaypointFromStack(ItemStack stack) {
        if (!stack.hasTag() || !stack.getTag().contains("Waypoints")) return;
        ListTag list = stack.getTag().getList("Waypoints", Tag.TAG_COMPOUND);
        if (!list.isEmpty()) list.remove(list.size() - 1);
    }

    private void saveWaypoints(
        ItemStack stack,
        Player player,
        ServerLevel level
    ) {
        UUID cellId = getActiveCellId(stack);
        if (cellId == null) return;

        CellRegistryV2 registry = CellRegistryV2.get(level);
        CellDataV2 cell = registry.getCell(cellId);
        if (cell == null) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Cell not found"
            );
            return;
        }

        List<BlockPos> waypoints = getWaypointsFromStack(stack);
        cell.setPathWaypoints(waypoints);
        registry.setDirty();

        SystemMessageManager.sendToPlayer(
            player,
            SystemMessageManager.MessageCategory.INFO,
            waypoints.size() +
                " waypoints saved to cell " +
                cellId.toString().substring(0, 8)
        );
    }

    // ==================== BLOCK INTERACTION ====================

    @Override
    public InteractionResult useOn(UseOnContext context) {
        Level level = context.getLevel();
        Player player = context.getPlayer();
        ItemStack stack = context.getItemInHand();
        BlockPos pos = context.getClickedPos();

        if (player == null) return InteractionResult.PASS;

        // Check if clicked block is a Cell Core
        BlockState clickedState = level.getBlockState(pos);
        if (clickedState.getBlock() instanceof BlockCellCore) {
            if (
                !level.isClientSide && level instanceof ServerLevel serverLevel
            ) {
                if (player.isShiftKeyDown()) {
                    if (isInWaypointMode(stack)) {
                        // Save waypoints and exit waypoint mode
                        saveWaypoints(stack, player, serverLevel);
                        exitWaypointMode(stack);
                    } else {
                        // Enter waypoint mode if cell core has a cell
                        BlockEntity be = level.getBlockEntity(pos);
                        if (
                            be instanceof CellCoreBlockEntity coreBE &&
                            coreBE.getCellId() != null
                        ) {
                            enterWaypointMode(stack, coreBE.getCellId());
                            SystemMessageManager.sendToPlayer(
                                player,
                                SystemMessageManager.MessageCategory.INFO,
                                "Waypoint mode: click blocks to add waypoints, Shift+RC Cell Core to save"
                            );
                        } else {
                            showCellCoreInfo(player, serverLevel, pos);
                        }
                    }
                } else {
                    rescanCellCore(player, serverLevel, pos);
                }
            }
            return InteractionResult.sidedSuccess(level.isClientSide);
        }

        // Right-click on block: cycle marker type
        return cycleMarkerType(stack, player, level);
    }

    @Override
    public InteractionResultHolder<ItemStack> use(
        Level level,
        Player player,
        InteractionHand hand
    ) {
        ItemStack stack = player.getItemInHand(hand);

        if (player.isShiftKeyDown()) {
            if (
                !level.isClientSide && level instanceof ServerLevel serverLevel
            ) {
                if (isInWaypointMode(stack)) {
                    // Shift+Right-click in air while in waypoint mode: cancel
                    exitWaypointMode(stack);
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.WARNING,
                        "Waypoint edit cancelled"
                    );
                } else {
                    // Shift+Right-click in air: delete selected cell
                    deleteSelectedCell(stack, player, serverLevel);
                }
            }
        } else {
            // Right-click in air: cycle marker type
            cycleMarkerType(stack, player, level);
        }
        return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
    }

    // ==================== CELL CORE ACTIONS ====================

    /**
     * Right-click Cell Core: rescan cell via flood-fill.
     */
    private void rescanCellCore(
        Player player,
        ServerLevel level,
        BlockPos corePos
    ) {
        BlockEntity be = level.getBlockEntity(corePos);
        if (!(be instanceof CellCoreBlockEntity coreBE)) return;

        FloodFillResult result = FloodFillAlgorithm.tryFill(level, corePos);

        if (!result.isSuccess()) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Rescan failed: " +
                    (result.getErrorKey() != null
                        ? result.getErrorKey()
                        : "unknown error")
            );
            return;
        }

        CellRegistryV2 registry = CellRegistryV2.get(level);
        UUID cellId = coreBE.getCellId();

        if (cellId != null) {
            CellDataV2 existing = registry.getCell(cellId);
            if (existing != null) {
                registry.rescanCell(cellId, result);

                // Update interior face on block entity
                if (result.getInteriorFace() != null) {
                    coreBE.setInteriorFace(result.getInteriorFace());
                }

                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.INFO,
                    "Cell rescanned: " +
                        result.getInterior().size() +
                        " interior, " +
                        result.getWalls().size() +
                        " walls, " +
                        result.getBeds().size() +
                        " beds, " +
                        result.getAnchors().size() +
                        " anchors, " +
                        result.getDoors().size() +
                        " doors"
                );
                return;
            }
        }

        // No existing cell — create new one
        CellDataV2 newCell = registry.createCell(corePos, result, null);
        coreBE.setCellId(newCell.getId());
        if (result.getInteriorFace() != null) {
            coreBE.setInteriorFace(result.getInteriorFace());
        }

        SystemMessageManager.sendToPlayer(
            player,
            SystemMessageManager.MessageCategory.INFO,
            "New cell created: " +
                result.getInterior().size() +
                " interior, " +
                result.getWalls().size() +
                " walls"
        );
    }

    /**
     * Shift+Right-click Cell Core: display cell info.
     */
    private void showCellCoreInfo(
        Player player,
        ServerLevel level,
        BlockPos corePos
    ) {
        BlockEntity be = level.getBlockEntity(corePos);
        if (!(be instanceof CellCoreBlockEntity coreBE)) return;

        UUID cellId = coreBE.getCellId();
        if (cellId == null) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.WARNING,
                "Cell Core has no linked cell (right-click to scan)"
            );
            return;
        }

        CellRegistryV2 registry = CellRegistryV2.get(level);
        CellDataV2 cell = registry.getCell(cellId);

        if (cell == null) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.WARNING,
                "Cell " +
                    cellId.toString().substring(0, 8) +
                    "... not found in registry"
            );
            return;
        }

        // Build info message
        StringBuilder info = new StringBuilder();
        info.append("--- Cell Info ---\n");
        info
            .append("ID: ")
            .append(cellId.toString().substring(0, 8))
            .append("...\n");
        if (cell.getName() != null) {
            info.append("Name: ").append(cell.getName()).append("\n");
        }
        info
            .append("State: ")
            .append(cell.getState().getSerializedName())
            .append("\n");
        info
            .append("Volume: ")
            .append(cell.getInteriorBlocks().size())
            .append(" blocks\n");
        info.append("Walls: ").append(cell.getWallBlocks().size());
        if (!cell.getBreachedPositions().isEmpty()) {
            info
                .append(" (")
                .append(cell.getBreachedPositions().size())
                .append(" breached)");
        }
        info.append("\n");
        info
            .append("Beds: ")
            .append(cell.getBeds().size())
            .append(", Anchors: ")
            .append(cell.getAnchors().size())
            .append(", Doors: ")
            .append(cell.getDoors().size())
            .append("\n");
        info
            .append("Prisoners: ")
            .append(cell.getPrisonerCount())
            .append("/4\n");

        if (cell.getOwnerId() != null) {
            info
                .append("Owner: ")
                .append(cell.getOwnerId().toString().substring(0, 8))
                .append("... (")
                .append(cell.getOwnerType().getSerializedName())
                .append(")");
        } else {
            info.append("Owner: none");
        }

        SystemMessageManager.sendToPlayer(
            player,
            SystemMessageManager.MessageCategory.INFO,
            info.toString()
        );
    }

    // ==================== ATTACK (LEFT-CLICK) ====================

    @Override
    public boolean onBlockStartBreak(
        ItemStack stack,
        BlockPos pos,
        Player player
    ) {
        Level level = player.level();
        BlockState state = level.getBlockState(pos);

        // Left-click on marker: show info about the marker (always, even in waypoint mode)
        if (
            state.getBlock() instanceof BlockMarker && !isInWaypointMode(stack)
        ) {
            if (!level.isClientSide) {
                showMarkerInfo(stack, player, level, pos);
            }
            return true;
        }

        if (isInWaypointMode(stack)) {
            if (!level.isClientSide) {
                if (player.isShiftKeyDown()) {
                    // Shift+Left-click in waypoint mode: remove last waypoint
                    removeLastWaypointFromStack(stack);
                    int remaining = getWaypointsFromStack(stack).size();
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.INFO,
                        "Last waypoint removed (" + remaining + " remaining)"
                    );
                } else {
                    // Left-click in waypoint mode: add waypoint
                    BlockPos waypointPos = pos.above();
                    addWaypointToStack(stack, waypointPos);
                    int count = getWaypointsFromStack(stack).size();
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.INFO,
                        "Waypoint " +
                            count +
                            " added at " +
                            waypointPos.toShortString()
                    );
                }
            }
            return true;
        }

        // Shift + Left-click: remove marker above this block (if exists)
        if (player.isShiftKeyDown()) {
            if (!level.isClientSide) {
                removeStructureMarker(stack, player, (ServerLevel) level, pos);
            }
            return true;
        }

        // Left-click on block: place structure marker above it
        if (!level.isClientSide) {
            placeStructureMarker(stack, player, (ServerLevel) level, pos);
        }
        return true;
    }

    /**
     * Place a structure marker (no cell needed).
     */
    private void placeStructureMarker(
        ItemStack stack,
        Player player,
        ServerLevel level,
        BlockPos pos
    ) {
        MarkerType type = getCurrentType(stack);

        // Structure markers don't need a cell
        BlockPos markerPos = pos.above();
        BlockState currentState = level.getBlockState(markerPos);

        if (
            !currentState.isAir() &&
            !(currentState.getBlock() instanceof BlockMarker)
        ) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Cannot place marker - block is occupied"
            );
            return;
        }

        // Place the marker block
        level.setBlock(
            markerPos,
            ModBlocks.MARKER.get().defaultBlockState(),
            3
        );

        // Set the marker type (no cell ID for structure markers)
        BlockEntity be = level.getBlockEntity(markerPos);
        if (be instanceof MarkerBlockEntity marker) {
            marker.setMarkerType(type);

            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.INFO,
                type.name() + " marker placed at " + markerPos.toShortString()
            );
        }
    }

    /**
     * Show info about a marker block.
     */
    private void showMarkerInfo(
        ItemStack stack,
        Player player,
        Level level,
        BlockPos pos
    ) {
        BlockEntity be = level.getBlockEntity(pos);
        if (be instanceof MarkerBlockEntity marker) {
            MarkerType type = marker.getMarkerType();
            UUID cellId = marker.getCellId();

            if (cellId != null) {
                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.INFO,
                    "Cell marker: " +
                        type.name() +
                        " (Cell: " +
                        cellId.toString().substring(0, 8) +
                        "...)"
                );
            } else {
                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.INFO,
                    "Structure marker: " + type.name() + " (no cell)"
                );
            }
        }
    }

    /**
     * Remove a structure marker above the clicked position.
     */
    private void removeStructureMarker(
        ItemStack stack,
        Player player,
        ServerLevel level,
        BlockPos pos
    ) {
        BlockPos markerPos = pos.above();
        BlockState state = level.getBlockState(markerPos);

        if (state.getBlock() instanceof BlockMarker) {
            BlockEntity be = level.getBlockEntity(markerPos);
            if (be instanceof MarkerBlockEntity marker) {
                // Only remove if it's a structure marker (no cell ID)
                if (marker.getCellId() == null) {
                    level.destroyBlock(markerPos, false);
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.INFO,
                        "Structure marker removed"
                    );
                } else {
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.ERROR,
                        "This is a cell marker - use Cell Wand to manage cells"
                    );
                }
            }
        } else {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.WARNING,
                "No marker above this position"
            );
        }
    }

    // ==================== ACTIONS ====================

    /**
     * Cycle through STRUCTURE marker types (ENTRANCE, PATROL, LOOT, SPAWNER, ...).
     */
    private InteractionResult cycleMarkerType(
        ItemStack stack,
        Player player,
        Level level
    ) {
        if (!level.isClientSide) {
            MarkerType current = getCurrentType(stack);
            MarkerType next = current.nextStructureType();
            setCurrentType(stack, next);

            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.INFO,
                "Structure marker: " + next.name()
            );
        }
        return InteractionResult.sidedSuccess(level.isClientSide);
    }

    private void deleteSelectedCell(
        ItemStack stack,
        Player player,
        ServerLevel level
    ) {
        UUID cellId = getActiveCellId(stack);
        if (cellId == null) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "No cell selected"
            );
            return;
        }

        CellRegistryV2 registry = CellRegistryV2.get(level);
        CellDataV2 cell = registry.getCell(cellId);

        if (cell == null) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "Cell not found"
            );
            setActiveCellId(stack, null);
            return;
        }

        BlockPos spawnPoint = cell.getSpawnPoint();
        BlockState state = level.getBlockState(spawnPoint);
        if (state.getBlock() instanceof BlockMarker) {
            level.destroyBlock(spawnPoint, false);
        }

        setActiveCellId(stack, null);

        SystemMessageManager.sendToPlayer(
            player,
            SystemMessageManager.MessageCategory.INFO,
            "Cell deleted"
        );
    }

    // ==================== TOOLTIP ====================

    private ChatFormatting getTypeColor(MarkerType type) {
        return switch (type) {
            case WALL -> ChatFormatting.BLUE;
            case ANCHOR -> ChatFormatting.RED;
            case BED -> ChatFormatting.LIGHT_PURPLE;
            case DOOR -> ChatFormatting.AQUA;
            case DELIVERY -> ChatFormatting.YELLOW;
            case ENTRANCE -> ChatFormatting.GREEN;
            case PATROL -> ChatFormatting.YELLOW;
            case LOOT -> ChatFormatting.GOLD;
            case SPAWNER -> ChatFormatting.DARK_RED;
            case TRADER_SPAWN -> ChatFormatting.LIGHT_PURPLE;
            case MAID_SPAWN -> ChatFormatting.LIGHT_PURPLE;
            case MERCHANT_SPAWN -> ChatFormatting.AQUA;
        };
    }

    @Override
    public void appendHoverText(
        ItemStack stack,
        @Nullable Level level,
        List<Component> tooltip,
        TooltipFlag flag
    ) {
        tooltip.add(
            Component.literal("ADMIN WAND").withStyle(
                ChatFormatting.LIGHT_PURPLE,
                ChatFormatting.BOLD
            )
        );
        tooltip.add(
            Component.literal(
                "Structure markers + Cell Core management"
            ).withStyle(ChatFormatting.GRAY)
        );

        MarkerType type = getCurrentType(stack);
        tooltip.add(
            Component.literal("Type: " + type.name()).withStyle(
                getTypeColor(type)
            )
        );

        tooltip.add(Component.literal(""));
        tooltip.add(
            Component.literal("Left-click: Place marker").withStyle(
                ChatFormatting.YELLOW
            )
        );
        tooltip.add(
            Component.literal("Shift+Left-click: Remove marker").withStyle(
                ChatFormatting.RED
            )
        );
        tooltip.add(
            Component.literal("Left-click on marker: Show info").withStyle(
                ChatFormatting.DARK_GRAY
            )
        );
        tooltip.add(
            Component.literal("Right-click: Cycle type").withStyle(
                ChatFormatting.DARK_GRAY
            )
        );
        tooltip.add(
            Component.literal("Right-click Cell Core: Rescan cell").withStyle(
                ChatFormatting.AQUA
            )
        );
        tooltip.add(
            Component.literal(
                "Shift+Right-click Cell Core: Cell info / Waypoint mode"
            ).withStyle(ChatFormatting.AQUA)
        );

        if (isInWaypointMode(stack)) {
            int count = getWaypointsFromStack(stack).size();
            tooltip.add(Component.literal(""));
            tooltip.add(
                Component.literal(
                    "WAYPOINT MODE (" + count + " points)"
                ).withStyle(ChatFormatting.YELLOW, ChatFormatting.BOLD)
            );
        }
    }

    @Override
    public boolean isFoil(ItemStack stack) {
        return true; // Always glowing to indicate admin item
    }
}
