package com.tiedup.remake.items.base;

import com.tiedup.remake.util.ItemNBTHelper;
import java.util.List;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Interface for bondage items that can be locked with padlocks.
 *
 * <p>Items implementing this interface can be locked to prevent removal
 * without a key or force action.
 *
 * <h2>Lock Safety Pattern</h2>
 * When an item is locked:
 * <ul>
 *   <li>Cannot be removed by normal means</li>
 *   <li>Cannot be replaced unless forced</li>
 *   <li>Must be unlocked with a key or master key</li>
 * </ul>
 *
 * <h2>Implementations</h2>
 * <ul>
 *   <li>{@link com.tiedup.remake.v2.bondage.items.AbstractV2BondageItem} - All V2 bondage items</li>
 *   <li>{@link com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem} - Data-driven items (via AbstractV2BondageItem)</li>
 * </ul>
 *
 * <h2>NBT Storage</h2>
 * Lock state is stored in NBT:
 * <pre>{@code
 * {
 *   "locked": true,
 *   "lockable": true,
 *   "lockedByKeyUUID": "uuid-string"  // UUID of the key that locked this item
 * }
 * }</pre>
 *
 * <h2>Key-Lock System</h2>
 * Each lock is tied to a specific key via UUID:
 * <ul>
 *   <li>When locked with a key, the key's UUID is stored</li>
 *   <li>Only the matching key (or master key) can unlock</li>
 *   <li>Master key bypasses UUID check</li>
 * </ul>
 *
 * @see ItemPadlock
 * @see ItemKey
 */
public interface ILockable {
    // ========== NBT CONSTANTS ==========

    /** NBT key for locked state */
    String NBT_LOCKED = "locked";

    /** NBT key for lockable state (can accept padlock) */
    String NBT_LOCKABLE = "lockable";

    /** NBT key for the UUID of the key that locked this item */
    String NBT_LOCKED_BY_KEY_UUID = "lockedByKeyUUID";

    // ========== LOCK STATE METHODS ==========

    /**
     * Set the locked state of this item.
     *
     * <p><b>CRITICAL:</b> When unlocking (state = false), some items may reset
     * their resistance to base value to prevent exploits.</p>
     *
     * @param stack The ItemStack to modify
     * @param state true to lock, false to unlock
     * @return The modified ItemStack (for chaining)
     */
    default ItemStack setLocked(ItemStack stack, boolean state) {
        ItemNBTHelper.setBoolean(stack, NBT_LOCKED, state);
        return stack;
    }

    /**
     * Check if this item is currently locked.
     *
     * @param stack The ItemStack to check
     * @return true if locked
     */
    default boolean isLocked(ItemStack stack) {
        return ItemNBTHelper.getBoolean(stack, NBT_LOCKED);
    }

    /**
     * Set whether this item can be locked (lockable state).
     *
     * <p>This is different from locked state:
     * <ul>
     *   <li>lockable = true: Item can accept a padlock</li>
     *   <li>locked = true: Item currently has a padlock on it</li>
     * </ul>
     *
     * @param stack The ItemStack to modify
     * @param state true to make lockable, false to prevent locking
     * @return The modified ItemStack (for chaining)
     */
    default ItemStack setLockable(ItemStack stack, boolean state) {
        ItemNBTHelper.setBoolean(stack, NBT_LOCKABLE, state);
        return stack;
    }

    /**
     * Check if this item can be locked.
     *
     * @param stack The ItemStack to check
     * @return true if lockable (can accept a padlock)
     */
    default boolean isLockable(ItemStack stack) {
        return ItemNBTHelper.getBoolean(stack, NBT_LOCKABLE);
    }

    // ========== TOOLTIP HELPER ==========

    /**
     * Append lock status tooltip to item hover text.
     * Provides consistent lock/lockable display across all lockable items.
     *
     * @param stack The ItemStack being displayed
     * @param tooltip The tooltip list to append to
     */
    default void appendLockTooltip(ItemStack stack, List<Component> tooltip) {
        if (isLockable(stack)) {
            if (isLocked(stack)) {
                tooltip.add(
                    Component.translatable(
                        "item.tiedup.tooltip.locked"
                    ).withStyle(ChatFormatting.RED)
                );
            } else {
                tooltip.add(
                    Component.translatable(
                        "item.tiedup.tooltip.lockable"
                    ).withStyle(ChatFormatting.GOLD)
                );
            }
        }
    }

    /**
     * Check if the padlock should be dropped when unlocking.
     *
     * <p>Default implementation returns true (drop padlock on unlock).</p>
     * <p>Some items may override this to consume the padlock permanently.</p>
     *
     * @return true if padlock should be dropped
     */
    default boolean dropLockOnUnlock() {
        return true;
    }

    /**
     * Check if this item can have a padlock attached via anvil.
     *
     * <p>Some items cannot have padlocks attached due to their nature:</p>
     * <ul>
     *   <li>Adhesive items (tape) - stick to themselves, no attachment point</li>
     *   <li>Organic items (slime, vine, web) - living/organic matter</li>
     * </ul>
     *
     * <p>Default implementation returns true (can attach padlock).</p>
     *
     * @return true if padlock can be attached
     */
    default boolean canAttachPadlock() {
        return true;
    }

    // ========== KEY-LOCK SYSTEM ==========

    /**
     * Get the UUID of the key that locked this item.
     *
     * @param stack The ItemStack to check
     * @return The UUID of the locking key, or null if not locked or locked without key
     */
    @Nullable
    default UUID getLockedByKeyUUID(ItemStack stack) {
        return ItemNBTHelper.getUUID(stack, NBT_LOCKED_BY_KEY_UUID);
    }

    /**
     * Set the key UUID that locks this item.
     *
     * <p>Setting a non-null keyUUID will also set locked=true.
     * Setting null will unlock the item (locked=false).</p>
     *
     * @param stack The ItemStack to modify
     * @param keyUUID The UUID of the key, or null to unlock
     */
    default void setLockedByKeyUUID(ItemStack stack, @Nullable UUID keyUUID) {
        if (stack.isEmpty()) return;
        ItemNBTHelper.setUUID(stack, NBT_LOCKED_BY_KEY_UUID, keyUUID);
        ItemNBTHelper.setBoolean(stack, NBT_LOCKED, keyUUID != null);
    }

    /**
     * Check if a key matches this item's lock.
     *
     * <p>Default implementation compares the stored keyUUID with the provided one.</p>
     *
     * @param stack The ItemStack to check
     * @param keyUUID The key UUID to test
     * @return true if the key matches this lock
     */
    default boolean matchesKey(ItemStack stack, UUID keyUUID) {
        if (keyUUID == null) return false;
        UUID lockedBy = getLockedByKeyUUID(stack);
        return lockedBy != null && lockedBy.equals(keyUUID);
    }

    // ========== STRUGGLE/LOCKPICK SYSTEM ==========

    /**
     * NBT key for jammed state.
     */
    String NBT_JAMMED = "jammed";

    /**
     * Check if the lock is jammed (lockpick failed critically).
     *
     * <p>When jammed, only struggle can unlock the item, lockpick is blocked.
     * Jam state is set when lockpick has a 2.5% critical failure.</p>
     *
     * @param stack The ItemStack to check
     * @return true if the lock is jammed
     */
    default boolean isJammed(ItemStack stack) {
        return ItemNBTHelper.getBoolean(stack, NBT_JAMMED);
    }

    /**
     * Set the jammed state of this item's lock.
     *
     * <p>When jammed, lockpick cannot be used on this item.
     * Only struggle can unlock a jammed lock.</p>
     *
     * @param stack The ItemStack to modify
     * @param jammed true to jam the lock, false to clear jam
     */
    default void setJammed(ItemStack stack, boolean jammed) {
        if (jammed) {
            ItemNBTHelper.setBoolean(stack, NBT_JAMMED, true);
        } else {
            ItemNBTHelper.remove(stack, NBT_JAMMED);
        }
    }

    // ========== LOCK BREAKING (struggle/force) ==========

    /**
     * Completely break/destroy the lock on an item.
     *
     * <p>Used when a padlock is destroyed through struggle or force.
     * This removes all lock-related state from the item:
     * <ul>
     *   <li>Unlocks the item (lockedByKeyUUID = null)</li>
     *   <li>Removes the lockable flag (no more padlock slot)</li>
     *   <li>Clears any jam state</li>
     * </ul>
     *
     * @param stack The ItemStack to break the lock on
     */
    default void breakLock(ItemStack stack) {
        if (stack.isEmpty()) return;
        setLockedByKeyUUID(stack, null);
        setLockable(stack, false);
        setJammed(stack, false);
    }
}
