package com.tiedup.remake.util;

import com.tiedup.remake.dialogue.GagTalkManager;
import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

/**
 * Centralized message dispatcher with earplug awareness.
 *
 * <p>This utility ensures that all player messages respect the earplug system.
 * Players with earplugs equipped will not receive messages sent through this dispatcher
 * (unless using the "system" variants which bypass the check).
 *
 * <p>Use cases:
 * <ul>
 *   <li>{@link #sendTo} - Direct message to a player (earplug-aware)</li>
 *   <li>{@link #sendFrom} - Message from one player to another (earplug-aware)</li>
 *   <li>{@link #broadcastToAll} - Broadcast to all players (earplug-aware per player)</li>
 *   <li>{@link #sendActionBar} - Action bar message (earplug-aware)</li>
 *   <li>{@link #sendSystemMessage} - Critical system message (bypasses earplugs)</li>
 * </ul>
 */
public final class MessageDispatcher {

    private MessageDispatcher() {
        // Utility class - no instantiation
    }

    // EARPLUG-AWARE METHODS

    /**
     * Send a message to a player (respects earplugs).
     *
     * <p>If the target player has earplugs equipped, the message will NOT be sent.
     *
     * @param target The player to send the message to
     * @param message The message component to send
     * @return true if the message was sent, false if blocked by earplugs
     */
    public static boolean sendTo(Player target, Component message) {
        if (target == null || message == null) {
            return false;
        }
        if (hasEarplugs(target)) {
            return false; // Blocked by earplugs
        }
        target.sendSystemMessage(message);
        return true;
    }

    /**
     * Send a message from one player to another (respects receiver's earplugs).
     *
     * <p>If the receiver has earplugs equipped, the message will NOT be sent.
     * The sender parameter is currently unused but available for future
     * features like sender notification when message is blocked.
     *
     * @param sender The player sending the message (for future feedback features)
     * @param receiver The player receiving the message
     * @param message The message component to send
     * @return true if the message was sent, false if blocked by earplugs
     */
    public static boolean sendFrom(
        Player sender,
        Player receiver,
        Component message
    ) {
        if (receiver == null || message == null) {
            return false;
        }
        if (hasEarplugs(receiver)) {
            // Future: Could notify sender that message was blocked
            return false;
        }
        receiver.sendSystemMessage(message);
        return true;
    }

    /**
     * Broadcast a message to all players on the server (respects individual earplugs).
     *
     * <p>Each player's earplug state is checked individually. Players with earplugs
     * will NOT receive the message.
     *
     * @param server The Minecraft server
     * @param message The message component to broadcast
     */
    public static void broadcastToAll(
        MinecraftServer server,
        Component message
    ) {
        if (server == null || message == null) {
            return;
        }
        for (ServerPlayer player : server.getPlayerList().getPlayers()) {
            sendTo(player, message);
        }
    }

    /**
     * Send a message to the action bar (respects earplugs).
     *
     * <p>Action bar messages appear above the hotbar and fade after a few seconds.
     * If the target has earplugs, the message will NOT be shown.
     *
     * @param target The player to send the action bar message to
     * @param message The message component to display
     * @return true if the message was sent, false if blocked by earplugs
     */
    public static boolean sendActionBar(Player target, Component message) {
        if (target == null || message == null) {
            return false;
        }
        if (hasEarplugs(target)) {
            return false;
        }
        target.displayClientMessage(message, true);
        return true;
    }

    /**
     * Send a chat message (not action bar) that respects earplugs.
     *
     * <p>Chat messages appear in the chat window and persist in chat history.
     *
     * @param target The player to send the chat message to
     * @param message The message component to display
     * @return true if the message was sent, false if blocked by earplugs
     */
    public static boolean sendChat(Player target, Component message) {
        if (target == null || message == null) {
            return false;
        }
        if (hasEarplugs(target)) {
            return false;
        }
        target.displayClientMessage(message, false);
        return true;
    }

    // BYPASS METHODS (ignore earplugs)

    /**
     * Send a critical system message that IGNORES earplugs.
     *
     * <p>Use this for important system notifications that the player MUST see,
     * such as teleport warnings, server announcements, or error messages.
     *
     * @param target The player to send the message to
     * @param message The message component to send
     */
    public static void sendSystemMessage(Player target, Component message) {
        if (target == null || message == null) {
            return;
        }
        target.sendSystemMessage(message);
    }

    /**
     * Send a critical action bar message that IGNORES earplugs.
     *
     * @param target The player to send the action bar message to
     * @param message The message component to display
     */
    public static void sendSystemActionBar(Player target, Component message) {
        if (target == null || message == null) {
            return;
        }
        target.displayClientMessage(message, true);
    }

    // INTERNAL HELPERS

    /**
     * Check if a player has earplugs equipped.
     *
     * @param player The player to check
     * @return true if the player has earplugs, false otherwise
     */
    private static boolean hasEarplugs(Player player) {
        if (player == null) {
            return false;
        }
        PlayerBindState state = PlayerBindState.getInstance(player);
        return state != null && state.hasEarplugs();
    }

    // NPC DIALOGUE METHODS

    /**
     * Send a dialogue message from an entity to a player.
     * Formatted as: "<EntityName> message"
     *
     * @param entity The entity speaking
     * @param player The player receiving the message
     * @param message The dialogue text
     * @return true if message was sent, false if blocked by earplugs
     */
    public static boolean talkTo(
        LivingEntity entity,
        Player player,
        String message
    ) {
        if (entity == null || player == null || message == null) {
            return false;
        }
        if (entity.level().isClientSide()) {
            return false;
        }
        if (hasEarplugs(player)) {
            return false;
        }

        // Apply gag talk if entity is a gagged NPC
        String finalMessage = message;
        if (
            entity instanceof
                com.tiedup.remake.entities.AbstractTiedUpNpc npc &&
            npc.isGagged()
        ) {
            ItemStack gag = npc.getEquipment(BodyRegionV2.MOUTH);
            IBondageState state = KidnappedHelper.getKidnappedState(npc);
            if (state != null && !gag.isEmpty()) {
                Component gagged = GagTalkManager.processGagMessage(
                    state,
                    gag,
                    message
                );
                finalMessage = gagged.getString();
            }
        }

        Component formattedMessage = Component.literal("")
            .append(Component.literal("<").withStyle(ChatFormatting.WHITE))
            .append(entity.getDisplayName().copy())
            .append(Component.literal("> ").withStyle(ChatFormatting.WHITE))
            .append(
                Component.literal(finalMessage).withStyle(ChatFormatting.GRAY)
            );

        player.displayClientMessage(formattedMessage, false);
        return true;
    }

    /**
     * Send an action message from an entity to a player.
     * Formatted as: "* EntityName action *"
     *
     * @param entity The entity performing the action
     * @param player The player receiving the message
     * @param action The action text
     * @return true if message was sent, false if blocked by earplugs
     */
    public static boolean actionTo(
        LivingEntity entity,
        Player player,
        String action
    ) {
        if (entity == null || player == null || action == null) {
            return false;
        }
        if (entity.level().isClientSide()) {
            return false;
        }
        if (hasEarplugs(player)) {
            return false;
        }

        Component formattedMessage = Component.literal("")
            .append(Component.literal("* ").withStyle(ChatFormatting.GRAY))
            .append(entity.getDisplayName().copy())
            .append(
                Component.literal(" " + action + " *").withStyle(
                    ChatFormatting.GRAY
                )
            );

        player.displayClientMessage(formattedMessage, false);
        return true;
    }

    /**
     * Send a dialogue message to all players within a radius.
     *
     * @param entity The entity speaking
     * @param message The dialogue text
     * @param radius The broadcast radius in blocks
     */
    public static void talkToNearby(
        LivingEntity entity,
        String message,
        double radius
    ) {
        if (entity == null || message == null) {
            return;
        }

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

        for (Player player : nearbyPlayers) {
            talkTo(entity, player, message);
        }
    }

    /**
     * Send an action message to all players within a radius.
     *
     * @param entity The entity performing the action
     * @param action The action text
     * @param radius The broadcast radius in blocks
     */
    public static void actionToNearby(
        LivingEntity entity,
        String action,
        double radius
    ) {
        if (entity == null || action == null) {
            return;
        }

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

        for (Player player : nearbyPlayers) {
            actionTo(entity, player, action);
        }
    }
}
