package com.tiedup.remake.entities;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.damsel.components.*;
import com.tiedup.remake.entities.skins.DamselSkinManager;
import com.tiedup.remake.entities.skins.Gender;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.state.ICaptor;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
 * EntityDamsel - Capturable female NPC with full bondage support.
 *
 * All shared NPC functionality (bondage, equipment, animation, pose, name, gender)
 * has been extracted to AbstractTiedUpNpc.
 *
 * <p><b>Damsel-specific features (remain here):</b></p>
 * <ul>
 *   <li>DamselAppearance (variant/skin system)</li>
 *   <li>DamselPersonalitySystem (personality, mood, training, commands)</li>
 *   <li>DamselAIController (damsel-specific AI goals)</li>
 *   <li>DamselDialogueHandler (dialogue cooldowns)</li>
 *   <li>DamselRewardTracker (savior/reward system)</li>
 *   <li>MenuProvider (inventory GUI)</li>
 *   <li>Personality-related IDialogueSpeaker methods</li>
 * </ul>
 */
public class EntityDamsel
    extends AbstractTiedUpNpc
    implements net.minecraft.world.MenuProvider
{

    // DAMSEL-SPECIFIC DATA SYNC

    /**
     * Personality type name (e.g., "TIMID", "FIERCE").
     * Synced to client for UI display.
     */
    public static final EntityDataAccessor<String> DATA_PERSONALITY_TYPE =
        SynchedEntityData.defineId(
            EntityDamsel.class,
            EntityDataSerializers.STRING
        );

    /**
     * Current active command (e.g., "FOLLOW", "STAY", "NONE").
     * Synced to client for UI display.
     */
    public static final EntityDataAccessor<String> DATA_ACTIVE_COMMAND =
        SynchedEntityData.defineId(
            EntityDamsel.class,
            EntityDataSerializers.STRING
        );

    // DAMSEL-SPECIFIC COMPONENTS

    /**
     * Manages visual appearance (variant, skin, gender, name, slim arms).
     */
    private final DamselAppearance appearance;

    /**
     * Manages all personality-related systems.
     */
    private DamselPersonalitySystem personalitySystem;

    /**
     * Manages all AI-related systems.
     */
    private DamselAIController aiController;

    /**
     * Manages all dialogue-related systems.
     */
    private DamselDialogueHandler dialogueHandler;

    /**
     * Orchestrates NBT serialization for all components.
     */
    private DamselDataSerializer serializer;

    /** Tracks savior and reward state */
    private final DamselRewardTracker rewardTracker = new DamselRewardTracker(
        this
    );

    // CONSTRUCTOR

    @Override
    protected IBondageHost createBondageHost() {
        return new com.tiedup.remake.entities.damsel.hosts.BondageHost(this);
    }

    public EntityDamsel(EntityType<? extends EntityDamsel> type, Level level) {
        super(type, level);
        this.appearance = new DamselAppearance(this);

        this.personalitySystem = new DamselPersonalitySystem(
            this,
            new com.tiedup.remake.entities.damsel.hosts.PersonalityTickContextHost(
                this
            )
        );

        this.aiController = new DamselAIController(
            this,
            new com.tiedup.remake.entities.damsel.hosts.AIHost(this)
        );

        // (already done in AbstractTiedUpNpc constructor)

        this.dialogueHandler = new DamselDialogueHandler(
            new com.tiedup.remake.entities.damsel.hosts.DialogueHost(this)
        );

        this.serializer = new DamselDataSerializer(this);

        // CRITICAL: Register AI goals now that aiController is initialized
        // (registerGoals() was called by Mob constructor but skipped due to null check)
        this.registerGoals();
    }

    // ENTITY INITIALIZATION

    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        // Damsel-specific personality data
        this.entityData.define(DATA_PERSONALITY_TYPE, "UNKNOWN");
        this.entityData.define(DATA_ACTIVE_COMMAND, "NONE");
    }

    @Override
    public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
        super.onSyncedDataUpdated(key);
        if (DATA_VARIANT_ID.equals(key)) {
            this.appearance.invalidateVariantCache();
        }
    }

    @Override
    public void onAddedToWorld() {
        super.onAddedToWorld();

        if (
            !this.level().isClientSide &&
            this.appearance.getVariantId().isEmpty()
        ) {
            Gender preferredGender = SettingsAccessor.getPreferredSpawnGender(
                this.level().getGameRules()
            );

            DamselVariant variant = DamselSkinManager.CORE.getVariantForEntity(
                this.getUUID(),
                preferredGender
            );
            this.setVariant(variant);

            TiedUpMod.LOGGER.debug(
                "[EntityDamsel] Spawned with variant: {}",
                variant.id()
            );
        }

        // Server-side only: Initialize personality if not already set (new spawn)
        if (
            !this.level().isClientSide &&
            this.personalitySystem.getPersonalityState() == null
        ) {
            this.personalitySystem.initializePersonality();
        }
    }

    /**
     * Register all AI goals for this entity.
     *
     * CRITICAL: This method is called by Mob constructor BEFORE our constructor finishes.
     * We must null-check aiController and defer goal registration until after initialization.
     */
    @Override
    protected void registerGoals() {
        if (aiController == null) {
            // Called during super() construction, before aiController is initialized
            // Goals will be registered manually after component initialization
            return;
        }
        aiController.registerGoals(this.goalSelector, this.targetSelector);
    }

    /**
     * Not used directly -- registerGoals() delegates to aiController.
     * Required by AbstractTiedUpNpc contract.
     */
    @Override
    protected void registerNpcGoals() {
        // Handled by registerGoals() override above
    }

    /**
     * Create attribute modifiers for EntityDamsel.
     * Called during entity type registration.
     */
    public static AttributeSupplier.Builder createAttributes() {
        return Mob.createMobAttributes()
            .add(Attributes.MAX_HEALTH, 20.0)
            .add(Attributes.MOVEMENT_SPEED, 0.25)
            .add(Attributes.KNOCKBACK_RESISTANCE, 0.7)
            .add(Attributes.FOLLOW_RANGE, 60.0);
    }

    // TICK (damsel-specific extensions)

    /**
     * Damsel-specific tick logic.
     * Called by AbstractTiedUpNpc.tick() after animation and bondage restoration.
     */
    @Override
    protected void tickSubclass() {
        aiController.tickCallForHelp();

        aiController.tickLeashTraction();

        personalitySystem.tickPersonality();

        personalitySystem.tickIdleDialogue();

        personalitySystem.tickApproachDetection();

        personalitySystem.tickEnvironmentDialogue();
    }

    // VARIANT SYSTEM (Delegated to DamselAppearance)

    @Nullable
    public DamselVariant getVariant() {
        return this.appearance.getVariant();
    }

    public void setVariant(DamselVariant variant) {
        this.appearance.setVariant(variant);
    }

    public String getVariantId() {
        return this.appearance.getVariantId();
    }

    // SKIN TEXTURE (ISkinnedEntity implementation)

    @Override
    public ResourceLocation getSkinTexture() {
        return this.appearance.getSkinTexture();
    }

    // NAME SYSTEM OVERRIDE
    // (DamselAppearance also sets custom name visible, delegate to it)

    @Override
    public String getNpcName() {
        return this.appearance.getNpcName();
    }

    @Override
    public void setNpcName(String name) {
        this.appearance.setNpcName(name);
    }

    // GENDER/SLIMARMS OVERRIDE
    // (DamselAppearance manages these for damsels)

    @Override
    public boolean hasSlimArms() {
        return this.appearance.hasSlimArms();
    }

    @Override
    public void setGender(Gender gender) {
        this.appearance.setGender(gender);
    }

    @Override
    public Gender getGender() {
        return this.appearance.getGender();
    }

    // COMPONENT ACCESSORS

    public DamselAppearance getAppearance() {
        return appearance;
    }

    public DamselPersonalitySystem getPersonalitySystem() {
        return personalitySystem;
    }

    public DamselRewardTracker getRewardTracker() {
        return rewardTracker;
    }

    // NBT PERSISTENCE (damsel-specific)

    @Override
    public void addAdditionalSaveData(CompoundTag tag) {
        super.addAdditionalSaveData(tag);
        serializer.save(tag);
    }

    @Override
    public void readAdditionalSaveData(CompoundTag tag) {
        super.readAdditionalSaveData(tag);
        serializer.load(tag);
    }

    // BONDAGE SERVICE OVERRIDES

    /**
     * Override hurt to intercept player attacks for bondage service.
     */
    @Override
    public boolean hurt(DamageSource source, float amount) {
        if (
            !this.level().isClientSide &&
            getBondageManager().handleDamageWithService(source, amount)
        ) {
            return false;
        }
        return super.hurt(source, amount);
    }

    /**
     * Override display name to show violet color when bondage service is active.
     */
    @Override
    public Component getDisplayName() {
        if (getBondageManager().isBondageServiceEnabled()) {
            return Component.literal(this.getNpcName()).withStyle(
                ChatFormatting.LIGHT_PURPLE
            );
        }
        return super.getDisplayName();
    }

    // DIALOGUE SYSTEM

    public void talkTo(Player player, String message) {
        com.tiedup.remake.dialogue.EntityDialogueManager.talkTo(
            this,
            player,
            message
        );
    }

    public void talkTo(
        Player player,
        com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category
    ) {
        com.tiedup.remake.dialogue.EntityDialogueManager.talkTo(
            this,
            player,
            category
        );
    }

    public void actionTo(Player player, String action) {
        com.tiedup.remake.dialogue.EntityDialogueManager.actionTo(
            this,
            player,
            action
        );
    }

    public void actionTo(
        Player player,
        com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category
    ) {
        com.tiedup.remake.dialogue.EntityDialogueManager.actionTo(
            this,
            player,
            category
        );
    }

    public void talkToPlayersInRadius(String message, int radius) {
        com.tiedup.remake.dialogue.EntityDialogueManager.talkToNearby(
            this,
            message,
            radius
        );
    }

    public void talkToPlayersInRadius(
        com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
        int radius
    ) {
        com.tiedup.remake.dialogue.EntityDialogueManager.talkToNearby(
            this,
            category,
            radius
        );
    }

    public boolean talkToPlayersInRadiusWithCooldown(
        com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
        int radius
    ) {
        return dialogueHandler.talkToPlayersInRadiusWithCooldown(
            category,
            radius
        );
    }

    public void actionToPlayersInRadius(String action, int radius) {
        com.tiedup.remake.dialogue.EntityDialogueManager.actionToNearby(
            this,
            action,
            radius
        );
    }

    public void actionToPlayersInRadius(
        com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
        int radius
    ) {
        com.tiedup.remake.dialogue.EntityDialogueManager.actionToNearby(
            this,
            category,
            radius
        );
    }

    // SAVIOR & REWARD SYSTEM

    public void setSavior(@Nullable Player player) {
        rewardTracker.setSavior(player);
    }

    public boolean isSavior(Entity entity) {
        return rewardTracker.isSavior(entity);
    }

    @Nullable
    public UUID getSaviorUUID() {
        return rewardTracker.getSaviorUUID();
    }

    public void setTiedBy(@Nullable Player player) {
        rewardTracker.setTiedBy(player);
    }

    @Nullable
    public UUID getTiedByUUID() {
        return rewardTracker.getTiedByUUID();
    }

    public boolean hasGivenReward() {
        return rewardTracker.hasGivenReward();
    }

    public void resetRewardState() {
        rewardTracker.reset();
    }

    public void rewardSavior(Player savior) {
        rewardTracker.rewardSavior(savior);
    }

    // PERSONALITY SYSTEM ACCESS

    @Nullable
    public com.tiedup.remake.personality.PersonalityState getPersonalityState() {
        if (
            this.personalitySystem.getPersonalityState() == null &&
            !this.level().isClientSide
        ) {
            personalitySystem.initializePersonality();
        }
        return this.personalitySystem.getPersonalityState();
    }

    public com.tiedup.remake.personality.PersonalityType getPersonalityType() {
        return personalitySystem.getPersonalityType();
    }

    public void setPersonalityType(
        com.tiedup.remake.personality.PersonalityType newType
    ) {
        personalitySystem.setPersonalityType(newType);
    }

    public com.tiedup.remake.personality.NpcCommand getActiveCommand() {
        return personalitySystem.getActiveCommand();
    }

    // ANTI-FLEE SYSTEM

    public long getLastWhipTime() {
        return personalitySystem.getLastWhipTime();
    }

    public void setLastWhipTime(long time) {
        personalitySystem.setLastWhipTime(time);
    }

    // COMMAND SYSTEM

    public boolean giveCommand(
        Player commander,
        com.tiedup.remake.personality.NpcCommand command,
        @Nullable net.minecraft.core.BlockPos targetPos
    ) {
        if (!this.hasCollar()) return false;

        ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
        if (!CollarHelper.isCollar(collar)) return false;
        if (!CollarHelper.isOwner(collar, commander.getUUID())) {
            if (!this.isGagged()) {
                com.tiedup.remake.dialogue.EntityDialogueManager.talkByDialogueId(
                    this,
                    commander,
                    "command.reject.not_master"
                );
            }
            return false;
        }

        if (this.personalitySystem.getPersonalityState() != null) {
            if (
                !this.personalitySystem.getPersonalityState().willObeyCommand(
                    commander,
                    command
                )
            ) {
                return false;
            }

            this.personalitySystem.getPersonalityState().setActiveCommand(
                command,
                commander.getUUID(),
                targetPos
            );
            personalitySystem.syncPersonalityData();
            return true;
        }
        return false;
    }

    public boolean giveCommandWithTwoTargets(
        Player commander,
        com.tiedup.remake.personality.NpcCommand command,
        @Nullable net.minecraft.core.BlockPos targetPos,
        @Nullable net.minecraft.core.BlockPos targetPos2
    ) {
        boolean success = giveCommand(commander, command, targetPos);
        if (
            success &&
            targetPos2 != null &&
            this.personalitySystem.getPersonalityState() != null
        ) {
            this.personalitySystem.getPersonalityState().setCommandTarget2(
                targetPos2
            );
            personalitySystem.syncPersonalityData();
        }
        return success;
    }

    public void cancelCommand() {
        if (this.personalitySystem.getPersonalityState() != null) {
            this.personalitySystem.getPersonalityState().clearCommand();
            personalitySystem.syncPersonalityData();
        }
    }

    // NPC INVENTORY ACCESS

    public net.minecraft.core.NonNullList<ItemStack> getNpcInventory() {
        return this.getInventoryManager().getNpcInventory();
    }

    public int getNpcInventorySize() {
        return this.getInventoryManager().getNpcInventorySize();
    }

    public void setNpcInventorySize(int newSize) {
        this.getInventoryManager().setNpcInventorySize(newSize);
    }

    public boolean hasEdibleInInventory() {
        return this.getInventoryManager().hasEdibleInInventory();
    }

    public boolean tryEatFromInventory() {
        return this.getInventoryManager().tryEatFromInventory(
            this.personalitySystem.getPersonalityState()
        );
    }

    public boolean tryEatFromChest(net.minecraft.core.BlockPos chestPos) {
        return this.getInventoryManager().tryEatFromChest(
            chestPos,
            this.personalitySystem.getPersonalityState()
        );
    }

    // DIRECT FEEDING SYSTEM

    public boolean feedByPlayer(Player player, ItemStack foodStack) {
        return this.getInventoryManager().feedByPlayer(
            player,
            foodStack,
            this.personalitySystem.getPersonalityState()
        );
    }

    // INTERACTION

    @Override
    protected net.minecraft.world.InteractionResult mobInteract(
        Player player,
        net.minecraft.world.InteractionHand hand
    ) {
        ItemStack heldItem = player.getItemInHand(hand);

        // Check if holding edible item
        if (heldItem.getItem().isEdible()) {
            if (!this.hasCollar()) {
                if (
                    player instanceof net.minecraft.server.level.ServerPlayer sp
                ) {
                    sp.displayClientMessage(
                        Component.translatable(
                            "entity.tiedup.damsel.needs_collar_to_feed"
                        ).withStyle(ChatFormatting.RED),
                        true
                    );
                }
                return net.minecraft.world.InteractionResult.FAIL;
            }
            ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
            if (CollarHelper.isCollar(collar)) {
                if (!CollarHelper.isOwner(collar, player)) {
                    if (
                        player instanceof
                            net.minecraft.server.level.ServerPlayer sp
                    ) {
                        sp.displayClientMessage(
                            Component.translatable(
                                "entity.tiedup.damsel.not_collar_owner"
                            ).withStyle(ChatFormatting.RED),
                            true
                        );
                    }
                    return net.minecraft.world.InteractionResult.FAIL;
                }
                if (this.feedByPlayer(player, heldItem)) {
                    return net.minecraft.world.InteractionResult.SUCCESS;
                }
                if (
                    player instanceof net.minecraft.server.level.ServerPlayer sp
                ) {
                    sp.displayClientMessage(
                        Component.translatable(
                            "entity.tiedup.damsel.cant_eat_now"
                        ).withStyle(ChatFormatting.RED),
                        true
                    );
                }
                return net.minecraft.world.InteractionResult.FAIL;
            }
        }

        // Shift + empty hand on collared NPC = open conversation
        if (
            !this.level().isClientSide() &&
            player.isShiftKeyDown() &&
            heldItem.isEmpty() &&
            this.hasCollar()
        ) {
            ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
            if (CollarHelper.isCollar(collar)) {
                if (
                    CollarHelper.isOwner(collar, player) &&
                    player instanceof
                        net.minecraft.server.level.ServerPlayer serverPlayer
                ) {
                    if (
                        com.tiedup.remake.dialogue.conversation.ConversationManager.openConversation(
                            this,
                            serverPlayer
                        )
                    ) {
                        return net.minecraft.world.InteractionResult.SUCCESS;
                    }
                }
            }
        }

        // Leash detach: if player right-clicks their leashed damsel, detach leash
        if (
            !this.level().isClientSide() &&
            this.isLeashed() &&
            this.getLeashHolder() == player
        ) {
            this.dropLeash(true, !player.getAbilities().instabuild);

            ICaptor currentCaptor = getBondageManager().getCaptor();
            if (currentCaptor != null) {
                currentCaptor.removeCaptive(this, false);
                getBondageManager().clearCaptor();
            }

            return net.minecraft.world.InteractionResult.SUCCESS;
        }

        return super.mobInteract(player, hand);
    }

    // MENU PROVIDER

    @Override
    @javax.annotation.Nullable
    public net.minecraft.world.inventory.AbstractContainerMenu createMenu(
        int containerId,
        net.minecraft.world.entity.player.Inventory playerInventory,
        Player player
    ) {
        return this.getInventoryManager().createMenu(
            containerId,
            playerInventory,
            player
        );
    }

    // LIFECYCLE - DEATH

    @Override
    public void die(DamageSource damageSource) {
        if (
            !this.level().isClientSide &&
            this.level() instanceof
                net.minecraft.server.level.ServerLevel serverLevel
        ) {
            UUID uuid = this.getUUID();

            com.tiedup.remake.cells.CellRegistryV2 cellRegistry =
                com.tiedup.remake.cells.CellRegistryV2.get(serverLevel);
            cellRegistry.releasePrisonerFromAllCells(uuid);

            com.tiedup.remake.prison.PrisonerManager manager =
                com.tiedup.remake.prison.PrisonerManager.get(serverLevel);
            com.tiedup.remake.prison.PrisonerState state = manager.getState(
                uuid
            );

            if (
                state == com.tiedup.remake.prison.PrisonerState.IMPRISONED ||
                state == com.tiedup.remake.prison.PrisonerState.WORKING
            ) {
                com.tiedup.remake.prison.service.EscapeMonitorService.get().escape(
                    serverLevel,
                    uuid,
                    "player_death"
                );
            }

            TiedUpMod.LOGGER.debug(
                "[EntityDamsel] {} died, cleaned up registries",
                getNpcName()
            );
        }
        super.die(damageSource);
    }

    // IDIALOGUESPEAKER - Personality-specific methods

    @Override
    public com.tiedup.remake.dialogue.SpeakerType getSpeakerType() {
        return com.tiedup.remake.dialogue.SpeakerType.DAMSEL;
    }

    @Override
    @Nullable
    public com.tiedup.remake.personality.PersonalityType getSpeakerPersonality() {
        if (
            personalitySystem != null &&
            personalitySystem.getPersonalityState() != null
        ) {
            return personalitySystem.getPersonalityState().getPersonality();
        }
        return null;
    }

    @Override
    public int getSpeakerMood() {
        if (
            personalitySystem != null &&
            personalitySystem.getPersonalityState() != null
        ) {
            return (int) personalitySystem.getPersonalityState().getMood();
        }
        return 50;
    }

    @Override
    @Nullable
    public String getTargetRelation(Player player) {
        if (hasCollar()) {
            ItemStack collar = getEquipment(BodyRegionV2.NECK);
            if (CollarHelper.isCollar(collar)) {
                if (CollarHelper.isOwner(collar, player)) {
                    return "master";
                }
            }
        }
        return null;
    }
}
