package com.tiedup.remake.compat.mca;

import com.tiedup.remake.compat.mca.ai.MCABondageAIController;
import com.tiedup.remake.compat.mca.ai.MCABondageAILevel;
import com.tiedup.remake.compat.mca.dialogue.MCADialogueManager;
import com.tiedup.remake.compat.mca.personality.MCAMoodManager;
import com.tiedup.remake.compat.mca.personality.MCAPersonality;
import com.tiedup.remake.compat.mca.personality.MCAPersonalityManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IRestrainable;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Central coordinator for all MCA-TiedUp integration.
 *
 * Responsibilities:
 * - Manages AI state for tied MCA villagers
 * - Coordinates sync operations
 * - Provides unified API for bondage operations
 * - Handles capture/release lifecycle
 *
 * This is the main entry point for all MCA bondage operations.
 * Other code should go through this manager rather than directly
 * manipulating capabilities or AI.
 */
public class MCABondageManager {

    // Singleton instance
    private static final MCABondageManager INSTANCE = new MCABondageManager();

    // Track AI controllers for villagers (weak ref to avoid memory leaks)
    private final Map<LivingEntity, MCABondageAIController> aiControllers =
        new WeakHashMap<>();

    private MCABondageManager() {
        // Private constructor for singleton
    }

    /**
     * Get the singleton instance.
     */
    public static MCABondageManager getInstance() {
        return INSTANCE;
    }

    // LIFECYCLE EVENTS

    /** Dialogue broadcast radius in blocks */
    private static final double DIALOGUE_RADIUS = 16.0;

    /**
     * Called when MCA villager is tied up.
     * Initializes or updates AI control and syncs state.
     *
     * @param villager The MCA villager entity
     * @param bind The bind item being applied
     */
    public void onVillagerTied(LivingEntity villager, ItemStack bind) {
        if (!MCACompat.isMCAVillager(villager)) return;

        MCAPersonality personality =
            MCAPersonalityManager.getInstance().getPersonality(villager);

        TiedUpMod.LOGGER.debug(
            "[MCA Manager] Villager tied: {} with {} (personality: {})",
            villager.getName().getString(),
            bind.getItem().getClass().getSimpleName(),
            personality.getMcaId()
        );

        // Update AI level with personality
        MCABondageAIController controller = getOrCreateAIController(villager);
        controller.setPersonality(personality);
        controller.updateAILevel();

        // Mood change
        MCAMoodManager.getInstance().onTied(villager);

        // Dialogue
        String dialogue = MCADialogueManager.getBeingTiedDialogue(villager);
        MCADialogueManager.broadcastDialogue(
            villager,
            dialogue,
            DIALOGUE_RADIUS
        );

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when MCA villager is untied.
     * Updates AI control and syncs state.
     *
     * @param villager The MCA villager entity
     */
    public void onVillagerUntied(LivingEntity villager) {
        if (!MCACompat.isMCAVillager(villager)) return;

        TiedUpMod.LOGGER.debug(
            "[MCA Manager] Villager untied: {}",
            villager.getName().getString()
        );

        // Update AI level (may restore normal behavior)
        MCABondageAIController controller = aiControllers.get(villager);
        if (controller != null) {
            controller.updateAILevel();

            // If fully free, cleanup controller
            if (controller.getCurrentLevel() == MCABondageAILevel.NONE) {
                controller.cleanup();
                aiControllers.remove(villager);
            }
        }

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when MCA villager is captured (leashed to a captor).
     *
     * @param villager The MCA villager entity
     * @param captor The entity capturing the villager
     */
    public void onVillagerCaptured(LivingEntity villager, Entity captor) {
        if (!MCACompat.isMCAVillager(villager)) return;

        TiedUpMod.LOGGER.debug(
            "[MCA Manager] Villager captured: {} by {}",
            villager.getName().getString(),
            captor.getName().getString()
        );

        // Update AI to include follow behavior
        MCABondageAIController controller = getOrCreateAIController(villager);
        controller.updateAILevel();

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when MCA villager is freed from capture.
     *
     * @param villager The MCA villager entity
     */
    public void onVillagerFreed(LivingEntity villager) {
        if (!MCACompat.isMCAVillager(villager)) return;

        TiedUpMod.LOGGER.debug(
            "[MCA Manager] Villager freed: {}",
            villager.getName().getString()
        );

        // Update AI level
        MCABondageAIController controller = aiControllers.get(villager);
        if (controller != null) {
            controller.updateAILevel();
        }

        // Mood change (happy to be freed, usually)
        MCAMoodManager.getInstance().onFreed(villager);

        // Dialogue
        String dialogue = MCADialogueManager.getFreedDialogue(villager);
        MCADialogueManager.broadcastDialogue(
            villager,
            dialogue,
            DIALOGUE_RADIUS
        );

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when collar is added/removed.
     *
     * @param villager The MCA villager entity
     * @param hasCollar Whether the villager now has a collar
     */
    public void onCollarChanged(LivingEntity villager, boolean hasCollar) {
        if (!MCACompat.isMCAVillager(villager)) return;

        TiedUpMod.LOGGER.debug(
            "[MCA Manager] Collar changed for {}: {}",
            villager.getName().getString(),
            hasCollar ? "added" : "removed"
        );

        // Update AI level
        MCABondageAIController controller = getOrCreateAIController(villager);
        controller.updateAILevel();

        // Mood and dialogue
        if (hasCollar) {
            MCAMoodManager.getInstance().onCollared(villager);
            String dialogue = MCADialogueManager.getCollarPutOnDialogue(
                villager
            );
            MCADialogueManager.broadcastDialogue(
                villager,
                dialogue,
                DIALOGUE_RADIUS
            );
        } else {
            MCAMoodManager.getInstance().onCollarRemoved(villager);
        }

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when gag state changes.
     *
     * @param villager The MCA villager entity
     * @param isGagged Whether the villager is now gagged
     */
    public void onGagChanged(LivingEntity villager, boolean isGagged) {
        if (!MCACompat.isMCAVillager(villager)) return;

        // Update AI level (gagged+blindfolded = OVERRIDE)
        MCABondageAIController controller = aiControllers.get(villager);
        if (controller != null) {
            controller.updateAILevel();
        }

        // Mood change
        if (isGagged) {
            MCAMoodManager.getInstance().onGagged(villager);
        }

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when blindfold state changes.
     *
     * @param villager The MCA villager entity
     * @param isBlindfolded Whether the villager is now blindfolded
     */
    public void onBlindfoldChanged(
        LivingEntity villager,
        boolean isBlindfolded
    ) {
        if (!MCACompat.isMCAVillager(villager)) return;

        // Update AI level (gagged+blindfolded = OVERRIDE)
        MCABondageAIController controller = aiControllers.get(villager);
        if (controller != null) {
            controller.updateAILevel();
        }

        // Mood change
        if (isBlindfolded) {
            MCAMoodManager.getInstance().onBlindfolded(villager);
        }

        // Sync to clients
        syncBondageState(villager);
    }

    /**
     * Called when any sensory restriction changes (gag/blindfold).
     * Legacy method for compatibility.
     *
     * @param villager The MCA villager entity
     */
    public void onSensoryRestrictionChanged(LivingEntity villager) {
        if (!MCACompat.isMCAVillager(villager)) return;

        // Update AI level (gagged+blindfolded = OVERRIDE)
        MCABondageAIController controller = aiControllers.get(villager);
        if (controller != null) {
            controller.updateAILevel();
        }

        // Sync to clients
        syncBondageState(villager);
    }

    // AI CONTROL

    /**
     * Get or create AI controller for villager.
     *
     * @param villager The MCA villager entity
     * @return The AI controller (never null for valid MCA villagers)
     */
    public MCABondageAIController getOrCreateAIController(
        LivingEntity villager
    ) {
        return aiControllers.computeIfAbsent(
            villager,
            MCABondageAIController::new
        );
    }

    /**
     * Get AI controller for villager if it exists.
     *
     * @param villager The MCA villager entity
     * @return The AI controller, or null if none exists
     */
    @Nullable
    public MCABondageAIController getAIController(LivingEntity villager) {
        return aiControllers.get(villager);
    }

    /**
     * Get current AI level for villager.
     *
     * @param villager The MCA villager entity
     * @return The AI level (NONE if no controller exists)
     */
    public MCABondageAILevel getAILevel(LivingEntity villager) {
        MCABondageAIController controller = aiControllers.get(villager);
        return controller != null
            ? controller.getCurrentLevel()
            : MCABondageAILevel.NONE;
    }

    /**
     * Force a specific AI level (for debugging/commands).
     *
     * @param villager The MCA villager entity
     * @param level The AI level to set
     */
    public void setAILevel(LivingEntity villager, MCABondageAILevel level) {
        MCABondageAIController controller = getOrCreateAIController(villager);
        controller.setLevel(level);
    }

    /**
     * Check if villager should have restricted AI.
     *
     * @param villager The MCA villager entity
     * @return true if AI is restricted in any way
     */
    public boolean shouldRestrictAI(LivingEntity villager) {
        return getAILevel(villager) != MCABondageAILevel.NONE;
    }

    // SYNC

    /**
     * Sync all bondage state to tracking clients.
     * Delegates to MCANetworkHandler.
     *
     * @param villager The MCA villager entity
     */
    public void syncBondageState(LivingEntity villager) {
        if (villager.level().isClientSide()) return;
        if (!MCACompat.isMCAVillager(villager)) return;

        villager
            .getCapability(MCACompat.MCA_KIDNAPPED)
            .ifPresent(cap -> {
                com.tiedup.remake.compat.mca.network.MCANetworkHandler.syncBondageState(
                    villager,
                    cap
                );
            });
    }

    /**
     * Sync bondage state to a specific player.
     * Used when player starts tracking the villager.
     *
     * @param villager The MCA villager entity
     * @param tracker The player to sync to
     */
    public void syncBondageStateTo(
        LivingEntity villager,
        net.minecraft.server.level.ServerPlayer tracker
    ) {
        if (villager.level().isClientSide()) return;
        if (!MCACompat.isMCAVillager(villager)) return;

        villager
            .getCapability(MCACompat.MCA_KIDNAPPED)
            .ifPresent(cap -> {
                com.tiedup.remake.compat.mca.network.MCANetworkHandler.syncBondageStateTo(
                    villager,
                    cap,
                    tracker
                );
            });
    }

    // QUERIES

    /**
     * Get IRestrainable adapter for MCA villager.
     * Convenience method that delegates to MCACompat.
     *
     * @param villager The MCA villager entity
     * @return IRestrainable adapter, or null if not an MCA villager
     */
    @Nullable
    public IRestrainable getKidnappedState(LivingEntity villager) {
        return MCACompat.getKidnappedState(villager);
    }

    /**
     * Check if entity is a managed MCA villager.
     *
     * @param entity The entity to check
     * @return true if this entity is tracked by the manager
     */
    public boolean isManaged(Entity entity) {
        if (!(entity instanceof LivingEntity living)) return false;
        return aiControllers.containsKey(living);
    }

    // CLEANUP

    /**
     * Remove all tracking for a villager.
     * Called when villager dies or is removed.
     *
     * @param villager The MCA villager entity
     */
    public void removeVillager(LivingEntity villager) {
        MCABondageAIController controller = aiControllers.remove(villager);
        if (controller != null) {
            controller.cleanup();
        }
    }

    /**
     * Clear all tracking data.
     * Called on world unload.
     */
    public void clearAll() {
        for (MCABondageAIController controller : aiControllers.values()) {
            controller.cleanup();
        }
        aiControllers.clear();
    }
}
