package com.tiedup.remake.entities.kidnapper.components;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.dialogue.EntityDialogueManager;
import com.tiedup.remake.state.IBondageState;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.Nullable;

/**
 * KidnapperAggressionSystem - Manages aggression tracking and immunity.
 *
 * Handles three aggression subsystems:
 * 1. **Escaped Target Memory** - Remember escapees for pursuit (30 seconds)
 * 2. **Fight Back System** - Track last attacker for retaliation (5 seconds)
 * 3. **Robbery Immunity** - Prevent repeated targeting after robbery (2 minutes)
 *
 * <p><b>Low complexity</b> - Simple state tracking with time-based expiration.</p>
 */
public class KidnapperAggressionSystem {

    // CONSTANTS

    /** How long to remember an escaped target (30 seconds = 600 ticks) */
    private static final long ESCAPED_TARGET_MEMORY = 600;

    /** Duration of robbed immunity (2 minutes = 2400 ticks) */
    private static final long ROBBED_IMMUNITY_DURATION = 2400L;

    /** How long to remember last attacker for fight-back (5 seconds = 100 ticks) */
    private static final int FIGHT_BACK_MEMORY = 100;

    // FIELDS

    /** Host callbacks */
    private final IAggressionHost host;

    // Escaped Target Tracking
    /** Entity that escaped from this kidnapper */
    @Nullable
    private LivingEntity escapedTarget = null;

    /** Tick count when captive escaped */
    private long escapedTargetTime = 0;

    // Fight Back System
    /** Last entity that attacked this kidnapper */
    @Nullable
    private LivingEntity lastAttacker = null;

    /** Tick count when last attacked */
    private int lastAttackTime = 0;

    // Robbery Immunity System
    /**
     * Map of player UUIDs to game time when immunity expires.
     * Players robbed by this kidnapper cannot be targeted again for 2 minutes.
     */
    private final Map<UUID, Long> robbedImmunity = new HashMap<>();

    // CONSTRUCTOR

    public KidnapperAggressionSystem(IAggressionHost host) {
        this.host = host;
    }

    // ESCAPED TARGET SYSTEM

    /**
     * Called when a captive escapes from this kidnapper.
     * Records the escaped entity for potential recapture.
     *
     * @param escaped The entity that escaped (IBondageState or LivingEntity)
     */
    public void onCaptiveEscaped(@Nullable Object escaped) {
        LivingEntity escapee = null;

        if (escaped instanceof IBondageState kidnapped) {
            if (kidnapped instanceof LivingEntity living) {
                escapee = living;
            }
        } else if (escaped instanceof LivingEntity living) {
            escapee = living;
        }

        if (escapee != null) {
            this.escapedTarget = escapee;
            this.escapedTargetTime = getCurrentTick();

            TiedUpMod.LOGGER.debug(
                "[KidnapperAggressionSystem] {} remembering escapee: {}",
                host.getNpcName(),
                escapee.getName().getString()
            );

            // Trigger alert dialogue
            host.talkToPlayersInRadius(
                EntityDialogueManager.DialogueCategory.CAPTURE_ESCAPE,
                20
            );
        }
    }

    /**
     * Get the escaped target if still remembered.
     * Returns null if memory expired or target is dead/removed.
     *
     * @return The escaped entity, or null if forgotten/dead
     */
    @Nullable
    public LivingEntity getEscapedTarget() {
        if (this.escapedTarget == null) {
            return null;
        }

        // Check if memory expired (30 seconds)
        long currentTick = getCurrentTick();
        if (currentTick - this.escapedTargetTime > ESCAPED_TARGET_MEMORY) {
            this.escapedTarget = null;
            this.escapedTargetTime = 0;
            return null;
        }

        // Check if target is still valid
        if (!this.escapedTarget.isAlive() || this.escapedTarget.isRemoved()) {
            this.escapedTarget = null;
            this.escapedTargetTime = 0;
            return null;
        }

        return this.escapedTarget;
    }

    /**
     * Clear the escaped target memory.
     */
    public void clearEscapedTarget() {
        this.escapedTarget = null;
        this.escapedTargetTime = 0;
    }

    // FIGHT BACK SYSTEM

    /**
     * Get the last entity that attacked this kidnapper.
     * Returns null if attack was more than 5 seconds ago.
     *
     * @return The attacker if recent, null otherwise
     */
    @Nullable
    public LivingEntity getLastAttacker() {
        if (this.lastAttacker == null) {
            return null;
        }

        // Check if attack was recent (5 seconds = 100 ticks)
        int currentTick = (int) getCurrentTick();
        if (currentTick - this.lastAttackTime > FIGHT_BACK_MEMORY) {
            return null;
        }

        return this.lastAttacker;
    }

    /**
     * Set the last attacker for fight-back mechanics.
     *
     * @param attacker The entity that attacked
     */
    public void setLastAttacker(LivingEntity attacker) {
        this.lastAttacker = attacker;
        this.lastAttackTime = (int) getCurrentTick();
    }

    // ROBBERY IMMUNITY SYSTEM

    /**
     * Grant robbery immunity to a player for 2 minutes.
     * Players with immunity cannot be targeted by this kidnapper.
     *
     * @param playerUUID The player's UUID
     */
    public void grantRobbedImmunity(UUID playerUUID) {
        long expirationTime = getCurrentTick() + ROBBED_IMMUNITY_DURATION;
        this.robbedImmunity.put(playerUUID, expirationTime);

        TiedUpMod.LOGGER.debug(
            "[KidnapperAggressionSystem] {} granted robbery immunity to player {}",
            host.getNpcName(),
            playerUUID
        );
    }

    /**
     * Check if a player has active robbery immunity.
     * Lazily removes expired immunity during check.
     *
     * @param playerUUID The player's UUID
     * @return true if player has active immunity
     */
    public boolean hasRobbedImmunity(UUID playerUUID) {
        Long expirationTime = this.robbedImmunity.get(playerUUID);
        if (expirationTime == null) {
            return false;
        }

        // Check if immunity expired (lazy cleanup)
        long currentTick = getCurrentTick();
        if (currentTick >= expirationTime) {
            this.robbedImmunity.remove(playerUUID);
            return false;
        }

        return true;
    }

    /**
     * Clear robbery immunity for a specific player.
     *
     * @param playerUUID The player's UUID
     */
    public void clearRobbedImmunity(UUID playerUUID) {
        this.robbedImmunity.remove(playerUUID);
    }

    /**
     * Clear all robbery immunities (e.g., on death/removal).
     */
    public void clearAllRobbedImmunities() {
        this.robbedImmunity.clear();
    }

    // DAMAGE PROCESSING

    /** Damage reduction multiplier against monsters (50% damage taken) */
    private static final float MONSTER_DAMAGE_REDUCTION = 0.5f;

    /**
     * Process incoming damage: track attacker, punish prisoners, expire protection,
     * apply monster damage reduction for camp kidnappers.
     *
     * @param source The damage source
     * @param amount The raw damage amount
     * @return The modified damage amount (0 or less means cancel the hurt)
     */
    public float processIncomingDamage(DamageSource source, float amount) {
        float finalAmount = amount;

        // Track the attacker for fight back system
        if (source.getEntity() instanceof LivingEntity attacker) {
            this.setLastAttacker(attacker);

            // Punish prisoners who dare to attack us
            if (
                host.level() != null &&
                !host.level().isClientSide &&
                attacker instanceof ServerPlayer player
            ) {
                host.punishAttackingPrisoner(player);

                // Expire PROTECTED status if player attacks a kidnapper
                // Attacking a kidnapper voids your safe-exit window
                if (host.level() instanceof ServerLevel serverLevel) {
                    com.tiedup.remake.prison.PrisonerManager pm =
                        com.tiedup.remake.prison.PrisonerManager.get(
                            serverLevel
                        );
                    if (
                        pm.getState(player.getUUID()) ==
                        com.tiedup.remake.prison.PrisonerState.PROTECTED
                    ) {
                        pm.expireProtection(
                            player.getUUID(),
                            serverLevel.getGameTime()
                        );
                        TiedUpMod.LOGGER.debug(
                            "[KidnapperAggressionSystem] Expired PROTECTED status for {} (attacked kidnapper)",
                            player.getName().getString()
                        );
                    }
                }
            }

            // Camp kidnappers take reduced damage from monsters (trained fighters)
            if (
                host.getAssociatedStructure() != null &&
                attacker instanceof net.minecraft.world.entity.monster.Monster
            ) {
                finalAmount = amount * MONSTER_DAMAGE_REDUCTION;
            }
        }

        return finalAmount;
    }

    // HELPER METHODS

    /**
     * Get current game tick from world.
     */
    private long getCurrentTick() {
        if (host.level() != null) {
            return host.level().getGameTime();
        }
        return 0;
    }

    // NBT SERIALIZATION

    /**
     * Save aggression data to NBT.
     * Note: Only saves robbery immunity map, not transient tracking fields.
     */
    public void saveToNBT(net.minecraft.nbt.CompoundTag tag) {
        // Save robbery immunity map
        if (!this.robbedImmunity.isEmpty()) {
            net.minecraft.nbt.CompoundTag immunityTag =
                new net.minecraft.nbt.CompoundTag();
            for (Map.Entry<UUID, Long> entry : this.robbedImmunity.entrySet()) {
                immunityTag.putLong(
                    entry.getKey().toString(),
                    entry.getValue()
                );
            }
            tag.put("RobbedImmunity", immunityTag);
        }
    }

    /**
     * Load aggression data from NBT.
     */
    public void loadFromNBT(net.minecraft.nbt.CompoundTag tag) {
        // Load robbery immunity map
        if (tag.contains("RobbedImmunity")) {
            net.minecraft.nbt.CompoundTag immunityTag = tag.getCompound(
                "RobbedImmunity"
            );
            for (String key : immunityTag.getAllKeys()) {
                try {
                    UUID playerUUID = UUID.fromString(key);
                    long expirationTime = immunityTag.getLong(key);
                    this.robbedImmunity.put(playerUUID, expirationTime);
                } catch (IllegalArgumentException e) {
                    TiedUpMod.LOGGER.warn(
                        "[KidnapperAggressionSystem] Invalid UUID in robbery immunity: {}",
                        key
                    );
                }
            }
        }
    }
}
