package com.tiedup.remake.network;

import com.tiedup.remake.compat.mca.network.PacketSyncMCABondage;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.network.action.PacketForceFeeding;
import com.tiedup.remake.network.action.PacketForceSeatModifier;
import com.tiedup.remake.network.action.PacketSetKnifeCutTarget;
import com.tiedup.remake.network.action.PacketStruggle;
import com.tiedup.remake.network.action.PacketTighten;
import com.tiedup.remake.network.action.PacketTying;
import com.tiedup.remake.network.action.PacketUntying;
import com.tiedup.remake.network.armorstand.PacketSyncArmorStandBondage;
import com.tiedup.remake.network.bounty.PacketDeleteBounty;
import com.tiedup.remake.network.bounty.PacketRequestBounties;
import com.tiedup.remake.network.bounty.PacketSendBounties;
import com.tiedup.remake.network.cell.PacketAssignCellToCollar;
import com.tiedup.remake.network.cell.PacketCellAction;
import com.tiedup.remake.network.cell.PacketCoreMenuAction;
import com.tiedup.remake.network.cell.PacketOpenCellManager;
import com.tiedup.remake.network.cell.PacketOpenCellSelector;
import com.tiedup.remake.network.cell.PacketOpenCoreMenu;
import com.tiedup.remake.network.cell.PacketRenameCell;
import com.tiedup.remake.network.cell.PacketRequestCellList;
import com.tiedup.remake.network.cell.PacketSyncCellData;
import com.tiedup.remake.network.conversation.PacketEndConversationC2S;
import com.tiedup.remake.network.conversation.PacketEndConversationS2C;
import com.tiedup.remake.network.conversation.PacketOpenConversation;
import com.tiedup.remake.network.conversation.PacketRequestConversation;
import com.tiedup.remake.network.conversation.PacketSelectTopic;
import com.tiedup.remake.network.item.PacketAdjustItem;
import com.tiedup.remake.network.item.PacketAdjustRemote;
import com.tiedup.remake.network.labor.PacketSyncLaborProgress;
import com.tiedup.remake.network.master.PacketMasterStateSync;
import com.tiedup.remake.network.master.PacketOpenPetRequestMenu;
import com.tiedup.remake.network.master.PacketPetRequest;
import com.tiedup.remake.network.merchant.PacketCloseMerchantScreen;
import com.tiedup.remake.network.merchant.PacketOpenMerchantScreen;
import com.tiedup.remake.network.merchant.PacketPurchaseTrade;
import com.tiedup.remake.network.minigame.PacketContinuousStruggleHold;
import com.tiedup.remake.network.minigame.PacketContinuousStruggleState;
import com.tiedup.remake.network.minigame.PacketContinuousStruggleStop;
import com.tiedup.remake.network.minigame.PacketLockpickAttempt;
import com.tiedup.remake.network.minigame.PacketLockpickMiniGameMove;
import com.tiedup.remake.network.minigame.PacketLockpickMiniGameResult;
import com.tiedup.remake.network.minigame.PacketLockpickMiniGameStart;
import com.tiedup.remake.network.minigame.PacketLockpickMiniGameState;
import com.tiedup.remake.network.personality.PacketDisciplineAction;
import com.tiedup.remake.network.personality.PacketNpcCommand;
import com.tiedup.remake.network.personality.PacketOpenCommandWandScreen;
import com.tiedup.remake.network.personality.PacketRequestNpcInventory;
import com.tiedup.remake.network.personality.PacketSlaveBeingFreed;
import com.tiedup.remake.network.selfbondage.PacketSelfBondage;
import com.tiedup.remake.network.slave.PacketMasterEquip;
import com.tiedup.remake.network.slave.PacketSlaveAction;
import com.tiedup.remake.network.slave.PacketSlaveItemManage;
import com.tiedup.remake.network.sync.PacketPlayTestAnimation;
import com.tiedup.remake.network.sync.PacketSyncBindState;
import com.tiedup.remake.network.sync.PacketSyncClothesConfig;
import com.tiedup.remake.network.sync.PacketSyncCollarRegistry;
import com.tiedup.remake.network.sync.PacketSyncEnslavement;
import com.tiedup.remake.network.sync.PacketSyncLeashProxy;
import com.tiedup.remake.network.sync.PacketSyncMovementStyle;
import com.tiedup.remake.network.sync.PacketSyncPetBedState;
import com.tiedup.remake.network.sync.PacketSyncStruggleState;
import com.tiedup.remake.network.trader.PacketBuyCaptive;
import com.tiedup.remake.network.trader.PacketOpenTraderScreen;
import com.tiedup.remake.rig.network.PacketPlayRigAnim;
import com.tiedup.remake.v2.bondage.network.PacketSyncV2Equipment;
import com.tiedup.remake.v2.bondage.network.PacketV2LockToggle;
import com.tiedup.remake.v2.bondage.network.PacketV2SelfEquip;
import com.tiedup.remake.v2.bondage.network.PacketV2SelfLock;
import com.tiedup.remake.v2.bondage.network.PacketV2SelfRemove;
import com.tiedup.remake.v2.bondage.network.PacketV2SelfUnlock;
import com.tiedup.remake.v2.bondage.network.PacketV2StruggleStart;
import com.tiedup.remake.v2.furniture.network.PacketFurnitureEscape;
import com.tiedup.remake.v2.furniture.network.PacketFurnitureForcemount;
import com.tiedup.remake.v2.furniture.network.PacketFurnitureLock;
import com.tiedup.remake.v2.furniture.network.PacketSyncFurnitureDefinitions;
import com.tiedup.remake.v2.furniture.network.PacketSyncFurnitureState;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel;

/**
 * Network handler for TiedUp mod.
 * Manages packet registration and sending.
 */
public class ModNetwork {

    private static final String PROTOCOL_VERSION = "2";

    public static final SimpleChannel CHANNEL =
        NetworkRegistry.newSimpleChannel(
            ResourceLocation.fromNamespaceAndPath(TiedUpMod.MOD_ID, "main"),
            () -> PROTOCOL_VERSION,
            PROTOCOL_VERSION::equals,
            PROTOCOL_VERSION::equals
        );

    private static int packetId = 0;

    /**
     * Register all packets. Called during mod initialization.
     * Order matters — packet IDs are assigned sequentially.
     */
    public static void register() {
        // Sync (S2C)
        reg(
            PacketSyncBindState.class,
            PacketSyncBindState::encode,
            PacketSyncBindState::decode,
            PacketSyncBindState::handle
        );
        reg(
            PacketSyncStruggleState.class,
            PacketSyncStruggleState::encode,
            PacketSyncStruggleState::decode,
            PacketSyncStruggleState::handle
        );
        reg(
            PacketSyncEnslavement.class,
            PacketSyncEnslavement::encode,
            PacketSyncEnslavement::decode,
            PacketSyncEnslavement::handle
        );
        reg(
            PacketSyncLeashProxy.class,
            PacketSyncLeashProxy::encode,
            PacketSyncLeashProxy::decode,
            PacketSyncLeashProxy::handle
        );
        reg(
            PacketSyncCollarRegistry.class,
            PacketSyncCollarRegistry::encode,
            PacketSyncCollarRegistry::decode,
            PacketSyncCollarRegistry::handle
        );
        reg(
            PacketSyncClothesConfig.class,
            PacketSyncClothesConfig::encode,
            PacketSyncClothesConfig::decode,
            PacketSyncClothesConfig::handle
        );
        reg(
            PacketSyncPetBedState.class,
            PacketSyncPetBedState::encode,
            PacketSyncPetBedState::decode,
            PacketSyncPetBedState::handle
        );
        reg(
            PacketSyncCellData.class,
            PacketSyncCellData::encode,
            PacketSyncCellData::decode,
            PacketSyncCellData::handle
        );
        reg(
            PacketSyncLaborProgress.class,
            PacketSyncLaborProgress::encode,
            PacketSyncLaborProgress::decode,
            PacketSyncLaborProgress::handle
        );
        reg(
            PacketSyncArmorStandBondage.class,
            PacketSyncArmorStandBondage::encode,
            PacketSyncArmorStandBondage::decode,
            PacketSyncArmorStandBondage::handle
        );
        reg(
            PacketPlayTestAnimation.class,
            PacketPlayTestAnimation::encode,
            PacketPlayTestAnimation::decode,
            PacketPlayTestAnimation::handle
        );
        reg(
            PacketSyncMCABondage.class,
            PacketSyncMCABondage::encode,
            PacketSyncMCABondage::decode,
            PacketSyncMCABondage::handle
        );

        // Actions (bidirectional)
        reg(
            PacketTying.class,
            PacketTying::encode,
            PacketTying::decode,
            PacketTying::handle
        );
        reg(
            PacketUntying.class,
            PacketUntying::encode,
            PacketUntying::decode,
            PacketUntying::handle
        );
        reg(
            PacketForceFeeding.class,
            PacketForceFeeding::encode,
            PacketForceFeeding::decode,
            PacketForceFeeding::handle
        );
        reg(
            PacketStruggle.class,
            PacketStruggle::encode,
            PacketStruggle::decode,
            PacketStruggle::handle
        );
        reg(
            PacketTighten.class,
            PacketTighten::encode,
            PacketTighten::decode,
            PacketTighten::handle
        );
        reg(
            PacketSetKnifeCutTarget.class,
            PacketSetKnifeCutTarget::encode,
            PacketSetKnifeCutTarget::decode,
            PacketSetKnifeCutTarget::handle
        );
        reg(
            PacketSelfBondage.class,
            PacketSelfBondage::encode,
            PacketSelfBondage::decode,
            PacketSelfBondage::handle
        );
        reg(
            PacketForceSeatModifier.class,
            PacketForceSeatModifier::encode,
            PacketForceSeatModifier::decode,
            PacketForceSeatModifier::handle
        );

        // Items (C2S)
        reg(
            PacketAdjustItem.class,
            PacketAdjustItem::encode,
            PacketAdjustItem::decode,
            PacketAdjustItem::handle
        );
        reg(
            PacketAdjustRemote.class,
            PacketAdjustRemote::encode,
            PacketAdjustRemote::decode,
            PacketAdjustRemote::handle
        );

        // Slave management
        reg(
            PacketSlaveAction.class,
            PacketSlaveAction::encode,
            PacketSlaveAction::decode,
            PacketSlaveAction::handle
        );
        reg(
            PacketSlaveItemManage.class,
            PacketSlaveItemManage::encode,
            PacketSlaveItemManage::decode,
            PacketSlaveItemManage::handle
        );
        reg(
            PacketSlaveBeingFreed.class,
            PacketSlaveBeingFreed::encode,
            PacketSlaveBeingFreed::decode,
            PacketSlaveBeingFreed::handle
        );

        // NPC commands
        reg(
            PacketNpcCommand.class,
            PacketNpcCommand::encode,
            PacketNpcCommand::decode,
            PacketNpcCommand::handle
        );
        reg(
            PacketOpenCommandWandScreen.class,
            PacketOpenCommandWandScreen::encode,
            PacketOpenCommandWandScreen::decode,
            PacketOpenCommandWandScreen::handle
        );
        reg(
            PacketRequestNpcInventory.class,
            PacketRequestNpcInventory::encode,
            PacketRequestNpcInventory::decode,
            PacketRequestNpcInventory::handle
        );
        reg(
            PacketDisciplineAction.class,
            PacketDisciplineAction::encode,
            PacketDisciplineAction::decode,
            PacketDisciplineAction::handle
        );

        // Bounty
        reg(
            PacketRequestBounties.class,
            PacketRequestBounties::encode,
            PacketRequestBounties::decode,
            PacketRequestBounties::handle
        );
        reg(
            PacketSendBounties.class,
            PacketSendBounties::encode,
            PacketSendBounties::decode,
            PacketSendBounties::handle
        );
        reg(
            PacketDeleteBounty.class,
            PacketDeleteBounty::encode,
            PacketDeleteBounty::decode,
            PacketDeleteBounty::handle
        );

        // Struggle mini-game
        reg(
            PacketContinuousStruggleState.class,
            PacketContinuousStruggleState::encode,
            PacketContinuousStruggleState::decode,
            PacketContinuousStruggleState::handle
        );
        reg(
            PacketContinuousStruggleHold.class,
            PacketContinuousStruggleHold::encode,
            PacketContinuousStruggleHold::decode,
            PacketContinuousStruggleHold::handle
        );
        reg(
            PacketContinuousStruggleStop.class,
            PacketContinuousStruggleStop::encode,
            PacketContinuousStruggleStop::decode,
            PacketContinuousStruggleStop::handle
        );

        // Lockpick mini-game
        reg(
            PacketLockpickMiniGameStart.class,
            PacketLockpickMiniGameStart::encode,
            PacketLockpickMiniGameStart::decode,
            PacketLockpickMiniGameStart::handle
        );
        reg(
            PacketLockpickMiniGameState.class,
            PacketLockpickMiniGameState::encode,
            PacketLockpickMiniGameState::decode,
            PacketLockpickMiniGameState::handle
        );
        reg(
            PacketLockpickMiniGameMove.class,
            PacketLockpickMiniGameMove::encode,
            PacketLockpickMiniGameMove::decode,
            PacketLockpickMiniGameMove::handle
        );
        reg(
            PacketLockpickAttempt.class,
            PacketLockpickAttempt::encode,
            PacketLockpickAttempt::decode,
            PacketLockpickAttempt::handle
        );
        reg(
            PacketLockpickMiniGameResult.class,
            PacketLockpickMiniGameResult::encode,
            PacketLockpickMiniGameResult::decode,
            PacketLockpickMiniGameResult::handle
        );

        // Merchant trading
        reg(
            PacketOpenMerchantScreen.class,
            PacketOpenMerchantScreen::encode,
            PacketOpenMerchantScreen::decode,
            PacketOpenMerchantScreen::handle
        );
        reg(
            PacketPurchaseTrade.class,
            PacketPurchaseTrade::encode,
            PacketPurchaseTrade::decode,
            PacketPurchaseTrade::handle
        );
        reg(
            PacketCloseMerchantScreen.class,
            PacketCloseMerchantScreen::encode,
            PacketCloseMerchantScreen::decode,
            PacketCloseMerchantScreen::handle
        );

        // Slave trader
        reg(
            PacketOpenTraderScreen.class,
            PacketOpenTraderScreen::encode,
            PacketOpenTraderScreen::decode,
            PacketOpenTraderScreen::handle
        );
        reg(
            PacketBuyCaptive.class,
            PacketBuyCaptive::encode,
            PacketBuyCaptive::decode,
            PacketBuyCaptive::handle
        );

        // Cell management
        reg(
            PacketOpenCellManager.class,
            PacketOpenCellManager::encode,
            PacketOpenCellManager::decode,
            PacketOpenCellManager::handle
        );
        reg(
            PacketCellAction.class,
            PacketCellAction::encode,
            PacketCellAction::decode,
            PacketCellAction::handle
        );
        reg(
            PacketRenameCell.class,
            PacketRenameCell::encode,
            PacketRenameCell::decode,
            PacketRenameCell::handle
        );
        reg(
            PacketAssignCellToCollar.class,
            PacketAssignCellToCollar::encode,
            PacketAssignCellToCollar::decode,
            PacketAssignCellToCollar::handle
        );
        reg(
            PacketRequestCellList.class,
            PacketRequestCellList::encode,
            PacketRequestCellList::decode,
            PacketRequestCellList::handle
        );
        reg(
            PacketOpenCellSelector.class,
            PacketOpenCellSelector::encode,
            PacketOpenCellSelector::decode,
            PacketOpenCellSelector::handle
        );
        reg(
            PacketOpenCoreMenu.class,
            PacketOpenCoreMenu::encode,
            PacketOpenCoreMenu::decode,
            (msg, ctx) -> msg.handle(ctx)
        );
        reg(
            PacketCoreMenuAction.class,
            PacketCoreMenuAction::encode,
            PacketCoreMenuAction::decode,
            PacketCoreMenuAction::handle
        );

        // Conversation
        reg(
            PacketOpenConversation.class,
            PacketOpenConversation::encode,
            PacketOpenConversation::decode,
            PacketOpenConversation::handle
        );
        reg(
            PacketSelectTopic.class,
            PacketSelectTopic::encode,
            PacketSelectTopic::decode,
            PacketSelectTopic::handle
        );
        reg(
            PacketEndConversationC2S.class,
            PacketEndConversationC2S::encode,
            PacketEndConversationC2S::decode,
            PacketEndConversationC2S::handle
        );
        reg(
            PacketEndConversationS2C.class,
            PacketEndConversationS2C::encode,
            PacketEndConversationS2C::decode,
            PacketEndConversationS2C::handle
        );
        reg(
            PacketRequestConversation.class,
            PacketRequestConversation::encode,
            PacketRequestConversation::decode,
            PacketRequestConversation::handle
        );

        // Master / pet
        reg(
            PacketMasterStateSync.class,
            PacketMasterStateSync::encode,
            PacketMasterStateSync::decode,
            PacketMasterStateSync::handle
        );
        reg(
            PacketOpenPetRequestMenu.class,
            PacketOpenPetRequestMenu::encode,
            PacketOpenPetRequestMenu::decode,
            PacketOpenPetRequestMenu::handle
        );
        reg(
            PacketPetRequest.class,
            PacketPetRequest::encode,
            PacketPetRequest::decode,
            PacketPetRequest::handle
        );

        // V2 bondage equipment
        reg(
            PacketSyncV2Equipment.class,
            PacketSyncV2Equipment::encode,
            PacketSyncV2Equipment::decode,
            PacketSyncV2Equipment::handle
        );
        reg(
            PacketV2SelfRemove.class,
            PacketV2SelfRemove::encode,
            PacketV2SelfRemove::decode,
            PacketV2SelfRemove::handle
        );
        reg(
            PacketV2StruggleStart.class,
            PacketV2StruggleStart::encode,
            PacketV2StruggleStart::decode,
            PacketV2StruggleStart::handle
        );
        reg(
            PacketV2LockToggle.class,
            PacketV2LockToggle::encode,
            PacketV2LockToggle::decode,
            PacketV2LockToggle::handle
        );
        reg(
            PacketV2SelfEquip.class,
            PacketV2SelfEquip::encode,
            PacketV2SelfEquip::decode,
            PacketV2SelfEquip::handle
        );
        reg(
            PacketV2SelfLock.class,
            PacketV2SelfLock::encode,
            PacketV2SelfLock::decode,
            PacketV2SelfLock::handle
        );
        reg(
            PacketV2SelfUnlock.class,
            PacketV2SelfUnlock::encode,
            PacketV2SelfUnlock::decode,
            PacketV2SelfUnlock::handle
        );
        reg(
            PacketMasterEquip.class,
            PacketMasterEquip::encode,
            PacketMasterEquip::decode,
            PacketMasterEquip::handle
        );

        // Furniture
        reg(
            PacketSyncFurnitureState.class,
            PacketSyncFurnitureState::encode,
            PacketSyncFurnitureState::decode,
            PacketSyncFurnitureState::handle
        );
        reg(
            PacketSyncFurnitureDefinitions.class,
            PacketSyncFurnitureDefinitions::encode,
            PacketSyncFurnitureDefinitions::decode,
            PacketSyncFurnitureDefinitions::handle
        );
        reg(
            PacketFurnitureLock.class,
            PacketFurnitureLock::encode,
            PacketFurnitureLock::decode,
            PacketFurnitureLock::handle
        );
        reg(
            PacketFurnitureForcemount.class,
            PacketFurnitureForcemount::encode,
            PacketFurnitureForcemount::decode,
            PacketFurnitureForcemount::handle
        );
        reg(
            PacketFurnitureEscape.class,
            PacketFurnitureEscape::encode,
            PacketFurnitureEscape::decode,
            PacketFurnitureEscape::handle
        );

        // Movement style
        reg(
            PacketSyncMovementStyle.class,
            PacketSyncMovementStyle::encode,
            PacketSyncMovementStyle::decode,
            PacketSyncMovementStyle::handle
        );

        // RIG animation system (S2C cinematic one-shot)
        reg(
            PacketPlayRigAnim.class,
            PacketPlayRigAnim::encode,
            PacketPlayRigAnim::decode,
            PacketPlayRigAnim::handleOnClient
        );

        TiedUpMod.LOGGER.info("Registered {} network packets", packetId);
    }

    private static <T> void reg(
        Class<T> clazz,
        BiConsumer<T, FriendlyByteBuf> encoder,
        Function<FriendlyByteBuf, T> decoder,
        BiConsumer<T, Supplier<NetworkEvent.Context>> handler
    ) {
        CHANNEL.registerMessage(nextId(), clazz, encoder, decoder, handler);
    }

    private static int nextId() {
        return packetId++;
    }

    public static void sendToPlayer(Object packet, ServerPlayer player) {
        CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), packet);
    }

    public static void sendToAllTracking(Object packet, ServerPlayer player) {
        CHANNEL.send(
            PacketDistributor.TRACKING_ENTITY.with(() -> player),
            packet
        );
    }

    public static void sendToAllTrackingEntity(
        Object packet,
        net.minecraft.world.entity.Entity entity
    ) {
        CHANNEL.send(
            PacketDistributor.TRACKING_ENTITY.with(() -> entity),
            packet
        );
    }

    public static void sendToAllTrackingAndSelf(
        Object packet,
        ServerPlayer player
    ) {
        CHANNEL.send(
            PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player),
            packet
        );
    }

    public static void sendToServer(Object packet) {
        CHANNEL.sendToServer(packet);
    }

    public static void sendToTracking(
        Object packet,
        net.minecraft.world.entity.Entity entity
    ) {
        CHANNEL.send(
            PacketDistributor.TRACKING_ENTITY.with(() -> entity),
            packet
        );
    }
}
