package com.tiedup.remake.dialogue;

import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.entities.EntityKidnapperMerchant;
import com.tiedup.remake.entities.KidnapperTheme;
import com.tiedup.remake.personality.PersonalityState;
import com.tiedup.remake.personality.PersonalityType;
import com.tiedup.remake.util.MessageDispatcher;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;

/**
 * Bridge between the legacy EntityDialogueManager and the new DialogueManager.
 *
 * Provides methods to:
 * - Build DialogueContext from EntityDamsel state
 * - Build DialogueContext from IDialogueSpeaker (universal)
 * - Map DialogueCategory to dialogue IDs
 * - Try data-driven dialogues first, fallback to hardcoded
 *
 * Personality System: Data-driven dialogue
 * Universal NPC Support: Extended for all speaker types
 */
public class DialogueBridge {

    /**
     * Build a DialogueContext from an EntityDamsel.
     *
     * @param entity The entity to build context from
     * @param player The player interacting (can be null)
     * @return DialogueContext with all available state
     */
    public static DialogueContext buildContext(
        EntityDamsel entity,
        @Nullable Player player
    ) {
        DialogueContext.Builder builder = DialogueContext.builder()
            .npcName(entity.getNpcName())
            .bound(entity.isTiedUp())
            .gagged(entity.isGagged())
            .blindfold(entity.isBlindfolded())
            .hasCollar(entity.hasCollar());

        // Add player info if available
        if (player != null) {
            builder.playerName(player.getName().getString());
        }

        // Add personality state if available
        PersonalityState state = entity.getPersonalityState();
        if (state != null) {
            builder
                .personality(state.getPersonality())
                .mood((int) state.getMood())
                .hunger(state.getNeeds().getHunger());

            // Get master name if collared (commanding player is the master)
            if (
                entity.hasCollar() &&
                state.getCommandingPlayer() != null &&
                player != null
            ) {
                // If the interacting player is the commanding player (master)
                if (player.getUUID().equals(state.getCommandingPlayer())) {
                    builder.masterName(player.getName().getString());
                }
            }
        } else {
            // Default values if no personality state
            builder.personality(PersonalityType.CALM).mood(50);
        }

        return builder.build();
    }

    /**
     * Map a DialogueCategory to a dialogue ID.
     *
     * @param category The legacy category
     * @return Dialogue ID for the new system
     */
    public static String categoryToDialogueId(DialogueCategory category) {
        return switch (category) {
            // Capture sequence
            case CAPTURE_START -> "capture.start";
            case CAPTURE_APPROACHING -> "capture.approaching";
            case CAPTURE_CHASE -> "capture.chase";
            case CAPTURE_TYING -> "capture.tying";
            case CAPTURE_TIED -> "capture.tied";
            case CAPTURE_GAGGING -> "capture.gagging";
            case CAPTURE_GAGGED -> "capture.gagged";
            case CAPTURE_ENSLAVED -> "capture.enslaved";
            case CAPTURE_ESCAPE -> "capture.escape";
            // Damsel specific
            case DAMSEL_PANIC -> "capture.panic";
            case DAMSEL_FLEE -> "capture.flee";
            case DAMSEL_CAPTURED -> "capture.captured";
            case DAMSEL_FREED -> "capture.freed";
            case DAMSEL_GREETING -> "idle.greeting";
            case DAMSEL_IDLE -> "idle.free";
            case DAMSEL_CALL_FOR_HELP -> "capture.call_for_help";
            // Slave management
            case SLAVE_TALK_RESPONSE -> "idle.slave_talk";
            case SLAVE_STRUGGLE -> "struggle.warned";
            case SLAVE_TRANSPORT -> "idle.transport";
            case SLAVE_ARRIVE_PRISON -> "idle.arrive_prison";
            case SLAVE_TIED_TO_POLE -> "idle.tied_to_pole";
            case PUNISH -> "idle.punish";
            // Sale system
            case SALE_WAITING -> "idle.sale_waiting";
            case SALE_ANNOUNCE -> "idle.sale_announce";
            case SALE_OFFER -> "idle.sale_offer";
            case SALE_COMPLETE -> "idle.sale_complete";
            case SALE_ABANDONED -> "idle.sale_abandoned";
            case SALE_KEPT -> "idle.sale_kept";
            // Job system
            case JOB_ASSIGNED -> "jobs.assigned";
            case JOB_HURRY -> "jobs.hurry";
            case JOB_COMPLETE -> "jobs.complete";
            case JOB_FAILED -> "jobs.failed";
            case JOB_LAST_CHANCE -> "jobs.last_chance";
            case JOB_KILL -> "jobs.kill";
            // Combat
            case ATTACKED_RESPONSE -> "combat.attacked_response";
            case ATTACK_SLAVE -> "combat.attack_slave";
            case GENERIC_THREAT -> "combat.threat";
            case GENERIC_TAUNT -> "combat.taunt";
            // General
            case FREED -> "idle.freed_captor";
            case GOODBYE -> "idle.goodbye";
            case GET_OUT -> "idle.get_out";
            // Personality system commands
            case COMMAND_ACCEPT -> "command.generic.accept";
            case COMMAND_REFUSE -> "command.generic.refuse";
            case COMMAND_HESITATE -> "command.generic.hesitate";
            // Needs
            case NEED_HUNGRY -> "needs.hungry";
            case NEED_TIRED -> "needs.tired";
            case NEED_UNCOMFORTABLE -> "needs.uncomfortable";
            case NEED_DIGNITY_LOW -> "needs.dignity_low";
            // Personality hints
            case PERSONALITY_HINT_TIMID -> "personality.hint";
            case PERSONALITY_HINT_GENTLE -> "personality.hint";
            case PERSONALITY_HINT_SUBMISSIVE -> "personality.hint";
            case PERSONALITY_HINT_CALM -> "personality.hint";
            case PERSONALITY_HINT_CURIOUS -> "personality.hint";
            case PERSONALITY_HINT_PROUD -> "personality.hint";
            case PERSONALITY_HINT_FIERCE -> "personality.hint";
            case PERSONALITY_HINT_DEFIANT -> "personality.hint";
            case PERSONALITY_HINT_PLAYFUL -> "personality.hint";
            case PERSONALITY_HINT_MASOCHIST -> "personality.hint";
            case PERSONALITY_HINT_SADIST -> "personality.hint";
            // Discipline responses (Training V2)
            case PRAISE_RESPONSE -> "discipline.praise";
            case SCOLD_RESPONSE -> "discipline.scold";
            case THREATEN_RESPONSE -> "discipline.threaten";
        };
    }

    /**
     * Get dialogue from the data-driven system.
     *
     * @param entity The entity speaking
     * @param player The player (can be null)
     * @param category The dialogue category
     * @return Dialogue text, or null if not found
     */
    @Nullable
    public static String getDataDrivenDialogue(
        EntityDamsel entity,
        @Nullable Player player,
        DialogueCategory category
    ) {
        if (!DialogueManager.isInitialized()) {
            return null;
        }

        DialogueContext context = buildContext(entity, player);
        String dialogueId = categoryToDialogueId(category);

        return DialogueManager.getDialogue(dialogueId, context);
    }

    /**
     * Get command dialogue from the data-driven system.
     *
     * @param entity The entity
     * @param player The player
     * @param commandName The command name (e.g., "follow", "stay")
     * @param accepted Whether the command was accepted
     * @param hesitated Whether the NPC hesitated
     * @return Dialogue text, or null if not found
     */
    @Nullable
    public static String getCommandDialogue(
        EntityDamsel entity,
        @Nullable Player player,
        String commandName,
        boolean accepted,
        boolean hesitated
    ) {
        if (!DialogueManager.isInitialized()) {
            return null;
        }

        DialogueContext context = buildContext(entity, player);

        String suffix;
        if (!accepted) {
            suffix = ".refuse";
        } else if (hesitated) {
            suffix = ".hesitate";
        } else {
            suffix = ".accept";
        }

        String dialogueId = "command." + commandName.toLowerCase() + suffix;
        return DialogueManager.getDialogue(dialogueId, context);
    }

    // UNIVERSAL SPEAKER METHODS (IDialogueSpeaker)

    /**
     * Build a DialogueContext from any IDialogueSpeaker.
     * Automatically detects speaker type and builds appropriate context.
     *
     * @param speaker The dialogue speaker
     * @param player The player interacting (can be null)
     * @return DialogueContext with all available state
     */
    public static DialogueContext buildContext(
        IDialogueSpeaker speaker,
        @Nullable Player player
    ) {
        DialogueContext.Builder builder = DialogueContext.builder().fromSpeaker(
            speaker,
            player
        );

        // Add speaker-type specific context
        LivingEntity entity = speaker.asEntity();

        // Kidnapper-specific: add theme
        if (entity instanceof EntityKidnapper kidnapper) {
            KidnapperTheme theme = kidnapper.getTheme();
            if (theme != null) {
                builder.kidnapperTheme(theme.name());
            }
        }

        // Merchant-specific: add mode
        if (entity instanceof EntityKidnapperMerchant merchant) {
            builder.merchantMode(merchant.isHostile() ? "HOSTILE" : "MERCHANT");
        }

        // Damsel-specific: add bondage state and personality state
        if (entity instanceof EntityDamsel damsel) {
            builder
                .bound(damsel.isTiedUp())
                .gagged(damsel.isGagged())
                .blindfold(damsel.isBlindfolded())
                .hasCollar(damsel.hasCollar());

            // Add needs from personality state
            PersonalityState state = damsel.getPersonalityState();
            if (state != null) {
                builder.hunger(state.getNeeds().getHunger());

                // Get master name if collared
                if (
                    damsel.hasCollar() &&
                    state.getCommandingPlayer() != null &&
                    player != null &&
                    player.getUUID().equals(state.getCommandingPlayer())
                ) {
                    builder.masterName(player.getName().getString());
                }
            }
        }

        return builder.build();
    }

    /**
     * Get dialogue for a speaker and dialogue ID.
     *
     * @param speaker The dialogue speaker
     * @param player The player (can be null)
     * @param dialogueId The dialogue ID (e.g., "guard.watching")
     * @return Dialogue text, or null if not found
     */
    @Nullable
    public static String getDialogue(
        IDialogueSpeaker speaker,
        @Nullable Player player,
        String dialogueId
    ) {
        if (!DialogueManager.isInitialized()) {
            return null;
        }

        DialogueContext context = buildContext(speaker, player);
        return DialogueManager.getDialogue(dialogueId, context);
    }

    /**
     * Send dialogue from a speaker to a player.
     * Handles dialogue lookup, variable substitution, and message dispatch.
     *
     * @param speaker The dialogue speaker
     * @param player The player to send to
     * @param dialogueId The dialogue ID
     * @return true if dialogue was sent, false if not found or on cooldown
     */
    public static boolean talkTo(
        IDialogueSpeaker speaker,
        Player player,
        String dialogueId
    ) {
        // Check cooldown
        if (speaker.getDialogueCooldown() > 0) {
            return false;
        }

        String dialogue = getDialogue(speaker, player, dialogueId);
        if (dialogue == null || dialogue.isEmpty()) {
            return false;
        }

        // Format and send message
        broadcastDialogue(speaker.asEntity(), player, dialogue, false);

        // Set default cooldown (600 ticks = 30 seconds)
        speaker.setDialogueCooldown(600);

        return true;
    }

    /**
     * Send an action from a speaker to a player.
     * Actions are formatted differently: "* Name action *"
     *
     * @param speaker The dialogue speaker
     * @param player The player to send to
     * @param dialogueId The dialogue ID
     * @return true if action was sent, false if not found or on cooldown
     */
    public static boolean actionTo(
        IDialogueSpeaker speaker,
        Player player,
        String dialogueId
    ) {
        // Check cooldown
        if (speaker.getDialogueCooldown() > 0) {
            return false;
        }

        String dialogue = getDialogue(speaker, player, dialogueId);
        if (dialogue == null || dialogue.isEmpty()) {
            return false;
        }

        // Format and send as action
        broadcastDialogue(speaker.asEntity(), player, dialogue, true);

        // Set default cooldown
        speaker.setDialogueCooldown(600);

        return true;
    }

    /**
     * Broadcast dialogue from an entity to a player.
     *
     * @param entity The speaking entity
     * @param player The target player
     * @param text The dialogue text
     * @param isAction Whether to format as action
     */
    private static void broadcastDialogue(
        LivingEntity entity,
        Player player,
        String text,
        boolean isAction
    ) {
        String name = entity.getName().getString();

        if (isAction) {
            MessageDispatcher.actionTo(entity, player, text);
        } else {
            MessageDispatcher.talkTo(entity, player, text);
        }
    }

    /**
     * Send dialogue to all players within a radius.
     *
     * @param speaker The dialogue speaker
     * @param dialogueId The dialogue ID
     * @param radius The broadcast radius in blocks
     * @return true if dialogue was sent to at least one player
     */
    public static boolean talkToNearby(
        IDialogueSpeaker speaker,
        String dialogueId,
        double radius
    ) {
        if (speaker.getDialogueCooldown() > 0) {
            return false;
        }

        LivingEntity entity = speaker.asEntity();
        var nearbyPlayers = entity
            .level()
            .getEntitiesOfClass(
                Player.class,
                entity.getBoundingBox().inflate(radius)
            );

        if (nearbyPlayers.isEmpty()) {
            return false;
        }

        boolean sentAny = false;
        for (Player player : nearbyPlayers) {
            String dialogue = getDialogue(speaker, player, dialogueId);
            if (dialogue != null && !dialogue.isEmpty()) {
                broadcastDialogue(entity, player, dialogue, false);
                sentAny = true;
            }
        }

        if (sentAny) {
            speaker.setDialogueCooldown(600);
        }

        return sentAny;
    }

    /**
     * Map theme to a personality-like folder for kidnapper dialogues.
     * This allows theme-based dialogue variation.
     *
     * @param theme The kidnapper theme
     * @return Personality folder name to use
     */
    public static String mapThemeToPersonalityFolder(KidnapperTheme theme) {
        if (theme == null) {
            return "default";
        }

        return switch (theme) {
            case ROPE, SHIBARI -> "traditional";
            case TAPE, LEATHER, CHAIN -> "rough";
            case MEDICAL, ASYLUM -> "clinical";
            case LATEX, RIBBON -> "playful";
            case BEAM, WRAP -> "default";
        };
    }
}
