package com.tiedup.remake.client.events;

import com.tiedup.remake.network.ModNetwork;
import com.tiedup.remake.network.selfbondage.PacketSelfBondage;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
import com.tiedup.remake.v2.bondage.component.ComponentType;
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Client-side event handler for self-bondage input.
 *
 * Intercepts left-click when holding bondage items and
 * sends packets continuously to the server to perform self-bondage.
 *
 * Self-bondage items:
 * - Binds (rope, chain, etc.) - Self-tie (requires holding left-click)
 * - Gags - Self-gag (if already tied, instant)
 * - Blindfolds - Self-blindfold (if already tied, instant)
 * - Mittens - Self-mitten (if already tied, instant)
 * - Earplugs - Self-earplug (if already tied, instant)
 * - Collar - NOT ALLOWED (cannot self-collar)
 */
@Mod.EventBusSubscriber(
    modid = "tiedup",
    value = Dist.CLIENT,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
@OnlyIn(Dist.CLIENT)
public class SelfBondageInputHandler {

    /** Track if we're currently in self-bondage mode */
    private static boolean isSelfBondageActive = false;

    /** The hand we're using for self-bondage */
    private static InteractionHand activeHand = null;

    /** Tick counter for packet sending interval */
    private static int tickCounter = 0;

    /** Send packet every 4 ticks (5 times per second) for smooth progress */
    private static final int PACKET_INTERVAL = 4;

    /**
     * Handle left-click in empty air - START self-bondage.
     */
    @SubscribeEvent
    public static void onLeftClickEmpty(
        PlayerInteractEvent.LeftClickEmpty event
    ) {
        startSelfBondage();
    }

    /**
     * Handle left-click on block - START self-bondage (cancel block breaking).
     */
    @SubscribeEvent
    public static void onLeftClickBlock(
        PlayerInteractEvent.LeftClickBlock event
    ) {
        if (!event.getLevel().isClientSide()) return;

        ItemStack stack = event.getItemStack();
        if (isSelfBondageItem(stack)) {
            event.setCanceled(true);
            startSelfBondage();
        }
    }

    /**
     * Start self-bondage mode if holding a bondage item.
     */
    private static void startSelfBondage() {
        LocalPlayer player = Minecraft.getInstance().player;
        if (player == null) return;

        // Check main hand first, then off hand
        InteractionHand hand = InteractionHand.MAIN_HAND;
        ItemStack stack = player.getMainHandItem();

        if (!isSelfBondageItem(stack)) {
            stack = player.getOffhandItem();
            hand = InteractionHand.OFF_HAND;

            if (!isSelfBondageItem(stack)) {
                return; // No bondage item in either hand
            }
        }

        // Start self-bondage mode
        isSelfBondageActive = true;
        activeHand = hand;
        tickCounter = 0;

        // Send initial packet immediately
        ModNetwork.sendToServer(new PacketSelfBondage(hand));
    }

    /**
     * Client tick - continuously send packets while attack button is held.
     */
    @SubscribeEvent
    public static void onClientTick(TickEvent.ClientTickEvent event) {
        if (event.phase != TickEvent.Phase.END) return;
        if (!isSelfBondageActive) return;

        Minecraft mc = Minecraft.getInstance();
        LocalPlayer player = mc.player;

        // Stop if conditions are no longer valid
        if (player == null || mc.screen != null) {
            stopSelfBondage();
            return;
        }

        // Check if attack button is still held
        if (!mc.options.keyAttack.isDown()) {
            stopSelfBondage();
            return;
        }

        // Check if still holding bondage item in the active hand
        ItemStack stack = player.getItemInHand(activeHand);
        if (!isSelfBondageItem(stack)) {
            stopSelfBondage();
            return;
        }

        // Send packet at interval for continuous progress
        tickCounter++;
        if (tickCounter >= PACKET_INTERVAL) {
            tickCounter = 0;
            ModNetwork.sendToServer(new PacketSelfBondage(activeHand));
        }
    }

    /**
     * Stop self-bondage mode.
     */
    private static void stopSelfBondage() {
        isSelfBondageActive = false;
        activeHand = null;
        tickCounter = 0;
    }

    /**
     * Check if a stack supports self-bondage.
     * Collar is explicitly excluded.
     */
    private static boolean isSelfBondageItem(ItemStack stack) {
        if (stack.isEmpty()) return false;

        // Collar cannot be self-equipped (V2 ownership component)
        if (CollarHelper.isCollar(stack)) {
            return false;
        }

        // V2 bondage items support self-bondage (left-click hold with tying duration)
        if (stack.getItem() instanceof IV2BondageItem) {
            return true;
        }

        // V2 data-driven items: check if it occupies any bondage region
        DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
        if (def != null) {
            return true;
        }

        // V1 fallback: bind items
        return BindModeHelper.isBindItem(stack)
            || DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null
            || DataDrivenBondageItem.getComponent(stack, ComponentType.BLINDING, BlindingComponent.class) != null;
    }
}
