package com.tiedup.remake.events.lifecycle;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityMaster;
import com.tiedup.remake.entities.ModEntities;
import com.tiedup.remake.state.IPlayerLeashAccess;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2BondageEquipmentProvider;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Handles clean player lifecycle transitions.
 * - Master/Slave links are cleaned up on disconnect.
 * - Pole leash positions are saved for restoration on reconnect.
 */
@Mod.EventBusSubscriber(modid = TiedUpMod.MOD_ID)
public class PlayerLifecycleHandler {

    /**
     * Triggered when a player leaves the server.
     * Cleans up enslavement links and saves pole leash state.
     *
     * Priority HIGH ensures this runs BEFORE other logout handlers that may read PlayerBindState.
     */
    @SubscribeEvent(priority = EventPriority.HIGH)
    public static void onPlayerLoggedOut(
        PlayerEvent.PlayerLoggedOutEvent event
    ) {
        Player player = event.getEntity();
        if (player.level().isClientSide) return;

        PlayerBindState state = PlayerBindState.getInstance(player);
        if (state == null) return;

        if (state.getCaptorManager().hasCaptives()) {
            TiedUpMod.LOGGER.info(
                "[Lifecycle] Captor {} disconnected. Freeing all captives.",
                player.getName().getString()
            );
            state.getCaptorManager().freeAllCaptives(true);
        }

        // CASE 2: This player is leashed
        if (
            player instanceof IPlayerLeashAccess access &&
            access.tiedup$isLeashed()
        ) {
            Entity holder = access.tiedup$getLeashHolder();

            // CASE 2a: Leashed to a pole - SAVE for restoration
            if (holder instanceof LeashFenceKnotEntity fenceKnot) {
                BlockPos polePos = fenceKnot.getPos();
                player
                    .getCapability(
                        V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT
                    )
                    .ifPresent(cap -> {
                        cap.savePoleLeash(polePos, player.level().dimension());
                        TiedUpMod.LOGGER.info(
                            "[Lifecycle] {} disconnected while leashed to pole at {}. Saved for restoration.",
                            player.getName().getString(),
                            polePos
                        );
                    });
                // Detach without dropping (will restore on reconnect)
                access.tiedup$detachLeash();
            }
            // MEDIUM FIX: CASE 2b: Leashed to a captor (player or NPC) - SAVE for restoration
            else if (state.isCaptive() && state.getCaptor() != null) {
                java.util.UUID captorUUID = state
                    .getCaptor()
                    .getEntity()
                    .getUUID();
                player
                    .getCapability(
                        V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT
                    )
                    .ifPresent(cap -> {
                        cap.saveCaptorUUID(captorUUID);
                        TiedUpMod.LOGGER.info(
                            "[Lifecycle] {} disconnected while captive to {}. Saved for restoration.",
                            player.getName().getString(),
                            captorUUID.toString().substring(0, 8)
                        );
                    });

                // Remove from captor's tracking list (will be re-added on reconnect)
                state.getCaptor().removeCaptive(state, false);

                // Detach leash without dropping (will restore on reconnect)
                access.tiedup$detachLeash();

                // Clear captor reference (will be restored on reconnect)
                state.setCaptor(null);
            }
        }

        // CASE 3: Pet play - handle Master entity
        handleMasterOnDisconnect(player);

        // Canonical removal point — this is the ONLY place removeInstance should be called for server-side logout
        PlayerBindState.removeInstance(player.getUUID(), false);
    }

    /**
     * Triggered when a player joins the server.
     * Restores Master entity if the player was a pet.
     */
    @SubscribeEvent(priority = EventPriority.LOW)
    public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
        Player player = event.getEntity();
        if (player.level().isClientSide) return;

        if (player instanceof ServerPlayer serverPlayer) {
            // Clean up any leftover pet bed/cage speed modifiers from previous session
            com.tiedup.remake.v2.blocks.PetBedManager.onPlayerLogin(
                serverPlayer
            );
            com.tiedup.remake.v2.blocks.PetCageManager.onPlayerLogin(
                serverPlayer
            );

            // Delay to ensure player is fully loaded and in world
            serverPlayer
                .getServer()
                .execute(() -> {
                    // Extra tick delay to ensure world is ready
                    serverPlayer
                        .level()
                        .getServer()
                        .execute(() -> {
                            handleMasterOnReconnect(serverPlayer);
                        });
                });
        }
    }

    // MASTER PERSISTENCE CONSTANTS

    private static final String NBT_MASTER_DATA = "TiedUp_MasterPersistence";
    private static final String NBT_MASTER_HAS_MASTER = "HasMaster";
    private static final String NBT_MASTER_VARIANT_ID = "MasterVariantId";
    private static final String NBT_MASTER_STATE = "MasterState";
    private static final String NBT_MASTER_NAME = "MasterName";

    // MASTER DISCONNECT HANDLING

    /**
     * Handle Master entity when pet player disconnects.
     * Saves Master data to player and removes Master entity.
     *
     * If Master entity is not found (in unloaded chunk), falls back to reading
     * data from the collar NBT which is continuously synchronized.
     */
    private static void handleMasterOnDisconnect(Player player) {
        // Check if player has a Master (pet play mode)
        UUID masterUUID = EntityMaster.getMasterUUID(player);
        if (masterUUID == null) return;

        if (!(player.level() instanceof ServerLevel serverLevel)) return;

        // O(1) lookup by UUID instead of scanning all entities
        EntityMaster master = null;
        Entity masterEntity = serverLevel.getEntity(masterUUID);
        if (masterEntity instanceof EntityMaster m) {
            master = m;
        }

        CompoundTag masterData = new CompoundTag();

        if (master != null) {
            // Master found - save from entity (most up-to-date)
            masterData.putBoolean(NBT_MASTER_HAS_MASTER, true);
            masterData.putString(
                NBT_MASTER_VARIANT_ID,
                master.getKidnapperVariantId()
            );
            masterData.putString(
                NBT_MASTER_STATE,
                master.getStateManager().serializeState()
            );
            masterData.putString(NBT_MASTER_NAME, master.getNpcName());

            TiedUpMod.LOGGER.info(
                "[Lifecycle] {} disconnected - saving Master {} (entity found)",
                player.getName().getString(),
                master.getNpcName()
            );

            // Despawn the Master entity
            master.discard();
        } else {
            // Master not found - fallback to collar data
            ItemStack collar =
                com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.getInRegion(
                    player,
                    com.tiedup.remake.v2.BodyRegionV2.NECK
                );

            if (!collar.isEmpty() && collar.hasTag()) {
                CompoundTag collarTag = collar.getTag();
                if (collarTag.getBoolean("masterDataValid")) {
                    masterData.putBoolean(NBT_MASTER_HAS_MASTER, true);
                    masterData.putString(
                        NBT_MASTER_VARIANT_ID,
                        collarTag.getString("masterVariantId")
                    );
                    masterData.putString(
                        NBT_MASTER_STATE,
                        collarTag.getString("masterState")
                    );
                    masterData.putString(
                        NBT_MASTER_NAME,
                        collarTag.getString("masterName")
                    );

                    TiedUpMod.LOGGER.warn(
                        "[Lifecycle] {} disconnected - Master entity not found in loaded chunks, " +
                            "using collar data. Master may remain in unloaded chunk.",
                        player.getName().getString()
                    );
                } else {
                    TiedUpMod.LOGGER.error(
                        "[Lifecycle] {} has Master UUID but no valid collar data!",
                        player.getName().getString()
                    );
                    return;
                }
            } else {
                TiedUpMod.LOGGER.error(
                    "[Lifecycle] {} has Master UUID but no collar found!",
                    player.getName().getString()
                );
                return;
            }
        }

        // Store in player's persistent data
        CompoundTag persistentData = player.getPersistentData();
        persistentData.put(NBT_MASTER_DATA, masterData);
    }

    // MASTER RECONNECT HANDLING

    /**
     * Handle Master restoration when pet player reconnects.
     * Spawns a new Master entity with saved data.
     *
     * First cleans up any orphaned Master entities that may remain from previous sessions
     * (e.g., if Master was in unloaded chunk during disconnect).
     */
    private static void handleMasterOnReconnect(ServerPlayer player) {
        // Check if player has saved Master data
        CompoundTag persistentData = player.getPersistentData();
        if (!persistentData.contains(NBT_MASTER_DATA)) return;

        CompoundTag masterData = persistentData.getCompound(NBT_MASTER_DATA);
        if (!masterData.getBoolean(NBT_MASTER_HAS_MASTER)) return;

        // Check if player still has pet collar
        if (!EntityMaster.hasPetCollar(player)) {
            // Player no longer has collar - clear saved data
            persistentData.remove(NBT_MASTER_DATA);
            TiedUpMod.LOGGER.info(
                "[Lifecycle] {} reconnected but no pet collar - clearing Master data",
                player.getName().getString()
            );
            return;
        }

        ServerLevel level = player.serverLevel();

        // CLEANUP: Remove orphaned Master entity that may remain from previous session
        // (e.g., if Master was in unloaded chunk during disconnect)
        // O(1) lookup by UUID from collar instead of scanning all entities
        UUID existingMasterUUID = EntityMaster.getMasterUUID(player);
        if (existingMasterUUID != null) {
            Entity orphanEntity = level.getEntity(existingMasterUUID);
            if (
                orphanEntity instanceof EntityMaster m && m.isPetPlayer(player)
            ) {
                TiedUpMod.LOGGER.warn(
                    "[Lifecycle] Found orphaned Master {} claiming {}, despawning before restoration",
                    m.getNpcName(),
                    player.getName().getString()
                );
                m.discard();
            }
        }

        // Spawn new Master entity near player
        EntityMaster master = ModEntities.MASTER.get().create(level);
        if (master == null) {
            TiedUpMod.LOGGER.error(
                "[Lifecycle] Failed to create Master entity for {}",
                player.getName().getString()
            );
            return;
        }

        // Restore Master data
        String variantId = masterData.getString(NBT_MASTER_VARIANT_ID);
        String stateSerialized = masterData.getString(NBT_MASTER_STATE);
        String masterName = masterData.getString(NBT_MASTER_NAME);

        if (!variantId.isEmpty()) {
            // Lookup and set the variant
            com.tiedup.remake.entities.KidnapperVariant variant =
                master.lookupVariantById(variantId);
            if (variant != null) {
                master.setKidnapperVariant(variant);
            }
        }

        // Position Master near player
        double angle = master.getRandom().nextDouble() * Math.PI * 2;
        double dist = 2.0 + master.getRandom().nextDouble() * 2;
        double x = player.getX() + Math.cos(angle) * dist;
        double z = player.getZ() + Math.sin(angle) * dist;

        master.setPos(x, player.getY(), z);

        // Set the pet player
        master.setPetPlayer(player);

        // Restore state if available
        if (!stateSerialized.isEmpty()) {
            master.getStateManager().deserializeState(stateSerialized);
        }

        // Spawn the Master
        level.addFreshEntity(master);

        // Update the player's collar with new Master UUID
        updateCollarMasterUUID(player, master.getUUID());

        TiedUpMod.LOGGER.info(
            "[Lifecycle] {} reconnected - restored Master {} at ({}, {}, {})",
            player.getName().getString(),
            master.getNpcName(),
            (int) x,
            (int) player.getY(),
            (int) z
        );

        // Clear saved Master data
        persistentData.remove(NBT_MASTER_DATA);
    }

    /**
     * Update the player's collar with the new Master UUID.
     */
    private static void updateCollarMasterUUID(
        ServerPlayer player,
        UUID masterUUID
    ) {
        net.minecraft.world.item.ItemStack collarStack =
            com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.getInRegion(
                player,
                com.tiedup.remake.v2.BodyRegionV2.NECK
            );

        if (!collarStack.isEmpty()) {
            CompoundTag tag = collarStack.getOrCreateTag();
            tag.putUUID("masterUUID", masterUUID);
        }
    }
}
