package com.tiedup.remake.v2.furniture;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.ModEntities;
import com.tiedup.remake.v2.bondage.V2BondageItems;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;

/**
 * Singleton item that spawns {@link EntityFurniture} on right-click.
 *
 * <p>Each ItemStack carries a {@link FurnitureRegistry#NBT_FURNITURE_ID} NBT tag
 * that determines which furniture definition to use. This follows the same pattern
 * as {@link com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem} where
 * a single registered Item serves all data-driven variants.</p>
 *
 * <p>On use, the item reads the furniture ID from NBT, validates that a
 * {@link FurnitureDefinition} exists in the registry, spawns an
 * {@link EntityFurniture} at the clicked position, and consumes the item
 * (unless in creative mode).</p>
 */
public class FurniturePlacerItem extends Item {

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

    // ===== PLACEMENT =====

    @Override
    public InteractionResult useOn(UseOnContext context) {
        Level level = context.getLevel();
        if (level.isClientSide) {
            return InteractionResult.SUCCESS;
        }

        ItemStack stack = context.getItemInHand();
        String furnitureIdStr = getFurnitureIdFromStack(stack);
        if (furnitureIdStr == null) {
            return InteractionResult.FAIL;
        }

        // Validate definition exists
        FurnitureDefinition def = FurnitureRegistry.get(furnitureIdStr);
        if (def == null) {
            TiedUpMod.LOGGER.warn(
                "[FurniturePlacerItem] Unknown furniture ID '{}', cannot place",
                furnitureIdStr
            );
            return InteractionResult.FAIL;
        }

        // Calculate placement position based on clicked face
        BlockPos clickedPos = context.getClickedPos();
        Direction face = context.getClickedFace();
        Vec3 spawnPos;

        if (face == Direction.UP) {
            // Clicked top of a block: place on top of it
            spawnPos = Vec3.atBottomCenterOf(clickedPos.above());
        } else if (face == Direction.DOWN) {
            // Clicked bottom of a block: place below it
            spawnPos = Vec3.atBottomCenterOf(clickedPos.below());
        } else {
            // Clicked a side: place adjacent to the clicked face
            spawnPos = Vec3.atBottomCenterOf(clickedPos.relative(face));
        }

        // Check floor_only placement restriction: must have solid ground below
        BlockPos spawnBlockPos = BlockPos.containing(spawnPos);
        if (def.floorOnly()) {
            BlockPos below = spawnBlockPos.below();
            if (!level.getBlockState(below).isSolidRender(level, below)) {
                return InteractionResult.FAIL;
            }
        }

        // Spawn the furniture entity
        EntityFurniture furniture = new EntityFurniture(
            ModEntities.FURNITURE.get(),
            level
        );
        furniture.setFurnitureId(furnitureIdStr);
        furniture.moveTo(spawnPos.x, spawnPos.y, spawnPos.z);

        // Face the same direction as the player (rounded to nearest 90 degrees)
        float yaw = 0f;
        if (context.getPlayer() != null) {
            float playerYaw = context.getPlayer().getYRot();
            yaw = Math.round(playerYaw / 90.0f) * 90.0f;
        }

        // Snap to wall: if enabled, check 4 cardinal directions for an adjacent wall
        // and rotate the furniture to face it (back against wall), overriding player yaw.
        if (def.snapToWall()) {
            Direction[] directions = {
                Direction.NORTH,
                Direction.SOUTH,
                Direction.EAST,
                Direction.WEST,
            };
            for (Direction dir : directions) {
                BlockPos wallPos = spawnBlockPos.relative(dir);
                if (
                    level
                        .getBlockState(wallPos)
                        .isFaceSturdy(level, wallPos, dir.getOpposite())
                ) {
                    yaw = dir.toYRot();
                    break;
                }
            }
        }

        furniture.setYRot(yaw);

        level.addFreshEntity(furniture);

        // Consume the item (unless creative)
        if (context.getPlayer() != null && !context.getPlayer().isCreative()) {
            stack.shrink(1);
        }

        return InteractionResult.CONSUME;
    }

    // ===== DISPLAY NAME =====

    @Override
    public Component getName(ItemStack stack) {
        String furnitureIdStr = getFurnitureIdFromStack(stack);
        if (furnitureIdStr == null) {
            return super.getName(stack);
        }

        FurnitureDefinition def = FurnitureRegistry.get(furnitureIdStr);
        if (def == null) {
            return super.getName(stack);
        }

        if (def.translationKey() != null) {
            return Component.translatable(def.translationKey());
        }
        return Component.literal(def.displayName());
    }

    // ===== FACTORY =====

    /**
     * Create an ItemStack for a specific furniture type.
     *
     * @param furnitureId the definition ID (must exist in {@link FurnitureRegistry})
     * @return a new ItemStack with the {@link FurnitureRegistry#NBT_FURNITURE_ID} NBT tag set,
     *         or {@link ItemStack#EMPTY} if the placer item is not yet registered
     */
    public static ItemStack createStack(ResourceLocation furnitureId) {
        if (V2BondageItems.FURNITURE_PLACER == null) return ItemStack.EMPTY;
        ItemStack stack = new ItemStack(V2BondageItems.FURNITURE_PLACER.get());
        stack
            .getOrCreateTag()
            .putString(
                FurnitureRegistry.NBT_FURNITURE_ID,
                furnitureId.toString()
            );
        return stack;
    }

    // ===== HELPERS =====

    /**
     * Read the furniture definition ID string from an ItemStack's NBT.
     *
     * @param stack the item stack to read from
     * @return the furniture ID string, or null if the tag is missing or empty
     */
    @Nullable
    public static String getFurnitureIdFromStack(ItemStack stack) {
        if (stack.isEmpty() || !stack.hasTag()) return null;
        // noinspection DataFlowIssue -- hasTag() guarantees non-null
        if (
            !stack.getTag().contains(FurnitureRegistry.NBT_FURNITURE_ID, 8)
        ) return null;
        String id = stack
            .getTag()
            .getString(FurnitureRegistry.NBT_FURNITURE_ID);
        return id.isEmpty() ? null : id;
    }
}
