package com.tiedup.remake.entities;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.dialogue.SpeakerType;
import com.tiedup.remake.entities.ai.kidnapper.KidnapperArcherRangedGoal;
import com.tiedup.remake.entities.skins.ArcherKidnapperSkinManager;
import com.tiedup.remake.entities.skins.Gender;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.monster.RangedAttackMob;
import net.minecraft.world.item.BowItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.Level;

/**
 * Archer Kidnapper - Ranged kidnapper that attacks with rope arrows.
 *
 *
 * Differences from regular EntityKidnapper:
 * - Attacks from range with rope arrows
 * - Lower health (15 vs 20)
 * - Faster movement for repositioning (0.30 vs 0.27)
 * - Only approaches to capture once target is tied
 * - Red name color (same as normal kidnapper)
 * - Dedicated archer skins
 * - Implements RangedAttackMob for bow animation
 *
 * Refactored: Uses parent's variant system via virtual methods.
 */
public class EntityKidnapperArcher
    extends EntityKidnapper
    implements RangedAttackMob
{

    // CONSTANTS

    public static final double ARCHER_MAX_HEALTH = 15.0D;
    public static final double ARCHER_MOVEMENT_SPEED = 0.30D;
    public static final double ARCHER_KNOCKBACK_RESISTANCE = 0.3D;
    public static final double ARCHER_FOLLOW_RANGE = 40.0D;
    public static final double ARCHER_ATTACK_DAMAGE = 4.0D;
    public static final int ARCHER_CAPTURE_TIME_TICKS = 25;
    public static final int ARCHER_NAME_COLOR = 0xFF5555;
    public static final float ARROW_VELOCITY = 1.6F;
    public static final float ARROW_INACCURACY = 2.0F;

    // DATA SYNC (Archer-specific)

    /**
     * Whether the archer is currently aiming (for bow draw animation).
     */
    private static final EntityDataAccessor<Boolean> DATA_AIMING =
        SynchedEntityData.defineId(
            EntityKidnapperArcher.class,
            EntityDataSerializers.BOOLEAN
        );

    /**
     * Whether the archer is in ranged attack mode (has active shooting target).
     * Used for bow visibility and ready pose.
     */
    private static final EntityDataAccessor<Boolean> DATA_IN_RANGED_MODE =
        SynchedEntityData.defineId(
            EntityKidnapperArcher.class,
            EntityDataSerializers.BOOLEAN
        );

    // STATE

    /** Ticks the archer has been charging the bow */
    private int aimingTicks;

    // HIT TRACKING (for cumulative bind chance)

    /** Maximum bind chance (100%) */
    public static final int ARCHER_MAX_BIND_CHANCE = 100;

    /** Track hit counts per target UUID */
    private final Map<UUID, Integer> targetHitCounts = new HashMap<>();

    // CONSTRUCTOR

    public EntityKidnapperArcher(
        EntityType<? extends EntityKidnapperArcher> type,
        Level level
    ) {
        super(type, level);
        // Force right-handed so bow is always in main hand (right)
        this.setLeftHanded(false);
    }

    // ATTRIBUTES

    /**
     * Create archer kidnapper attributes.
     * Fast but fragile - relies on range.
     */
    public static AttributeSupplier.Builder createAttributes() {
        return Mob.createMobAttributes()
            .add(Attributes.MAX_HEALTH, ARCHER_MAX_HEALTH)
            .add(Attributes.MOVEMENT_SPEED, ARCHER_MOVEMENT_SPEED)
            .add(Attributes.KNOCKBACK_RESISTANCE, ARCHER_KNOCKBACK_RESISTANCE)
            .add(Attributes.FOLLOW_RANGE, ARCHER_FOLLOW_RANGE)
            .add(Attributes.ATTACK_DAMAGE, ARCHER_ATTACK_DAMAGE);
    }

    // DATA SYNC

    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.define(DATA_AIMING, false);
        this.entityData.define(DATA_IN_RANGED_MODE, false);
    }

    // VARIANT SYSTEM - Override virtual methods

    @Override
    public KidnapperVariant lookupVariantById(String variantId) {
        return ArcherKidnapperSkinManager.CORE.getVariant(variantId);
    }

    @Override
    public KidnapperVariant computeVariantForEntity(UUID entityUUID) {
        Gender preferredGender = SettingsAccessor.getPreferredSpawnGender(
            this.level() != null ? this.level().getGameRules() : null
        );
        return ArcherKidnapperSkinManager.CORE.getVariantForEntity(
            entityUUID,
            preferredGender
        );
    }

    @Override
    public String getVariantTextureFolder() {
        return "textures/entity/kidnapper/archer/";
    }

    @Override
    public String getDefaultVariantId() {
        return "bowy";
    }

    @Override
    public void applyVariantName(KidnapperVariant variant) {
        // Archer variants always use their default name
        this.setNpcName(variant.defaultName());
    }

    @Override
    public String getVariantNBTKey() {
        // Use different key for backward compatibility with existing saves
        return "ArcherVariantName";
    }

    // AI GOALS

    @Override
    protected void registerGoals() {
        // Call parent to register all standard kidnapper goals
        super.registerGoals();

        // Add ranged attack goal with priority 1 (same as FindTarget)
        // This makes the archer shoot first, then approach to capture
        this.goalSelector.addGoal(1, new KidnapperArcherRangedGoal(this));

        TiedUpMod.LOGGER.debug(
            "[EntityKidnapperArcher] Registered ranged attack goal"
        );
    }

    // INITIALIZATION

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

        // Server-side: Give archer a bow if not holding one
        if (!this.level().isClientSide && this.getMainHandItem().isEmpty()) {
            this.setItemInHand(
                InteractionHand.MAIN_HAND,
                new ItemStack(Items.BOW)
            );
            TiedUpMod.LOGGER.debug(
                "[EntityKidnapperArcher] Gave bow to {}",
                this.getNpcName()
            );
        }
    }

    // RANGED ATTACK MOB INTERFACE

    /**
     * Called by the ranged goal to perform the actual attack.
     * Required by RangedAttackMob interface.
     */
    @Override
    public void performRangedAttack(LivingEntity target, float velocity) {
        // Create and shoot rope arrow
        EntityRopeArrow arrow = new EntityRopeArrow(this.level(), this);

        double dx = target.getX() - this.getX();
        double dy = target.getY(0.33) - arrow.getY();
        double dz = target.getZ() - this.getZ();
        double horizontalDist = Math.sqrt(dx * dx + dz * dz);

        arrow.shoot(
            dx,
            dy + horizontalDist * 0.2,
            dz,
            ARROW_VELOCITY,
            ARROW_INACCURACY
        );

        // Play bow sound
        this.level().playSound(
            null,
            this.getX(),
            this.getY(),
            this.getZ(),
            net.minecraft.sounds.SoundEvents.ARROW_SHOOT,
            this.getSoundSource(),
            1.0F,
            1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)
        );

        this.level().addFreshEntity(arrow);

        TiedUpMod.LOGGER.debug(
            "[EntityKidnapperArcher] {} shot arrow at {}",
            this.getNpcName(),
            target.getName().getString()
        );
    }

    // AIMING STATE (for animation)

    /**
     * Set whether the archer is currently aiming.
     */
    public void setAiming(boolean aiming) {
        if (aiming) {
            this.aimingTicks = 0;
            this.startUsingItem(InteractionHand.MAIN_HAND);
        } else {
            this.aimingTicks = 0;
            this.stopUsingItem();
        }
        this.entityData.set(DATA_AIMING, aiming);
    }

    /**
     * Check if the archer is currently aiming.
     */
    public boolean isAiming() {
        return this.entityData.get(DATA_AIMING);
    }

    /**
     * Set whether the archer is in ranged attack mode.
     */
    public void setInRangedMode(boolean inRangedMode) {
        this.entityData.set(DATA_IN_RANGED_MODE, inRangedMode);
    }

    /**
     * Check if the archer is in ranged attack mode.
     */
    public boolean isInRangedMode() {
        return this.entityData.get(DATA_IN_RANGED_MODE);
    }

    /**
     * Get how long the archer has been aiming (for bow pull animation).
     */
    public int getAimingTicks() {
        return this.aimingTicks;
    }

    @Override
    public void tick() {
        super.tick();
        if (this.isAiming()) {
            this.aimingTicks++;
        }
    }

    @Override
    public boolean isUsingItem() {
        return this.isAiming() || super.isUsingItem();
    }

    @Override
    public InteractionHand getUsedItemHand() {
        if (this.isAiming()) {
            return InteractionHand.MAIN_HAND;
        }
        return super.getUsedItemHand();
    }

    /**
     * Get the use animation type for the item.
     */
    public UseAnim getItemUseAnimation() {
        if (
            this.isAiming() &&
            this.getMainHandItem().getItem() instanceof BowItem
        ) {
            return UseAnim.BOW;
        }
        return UseAnim.NONE;
    }

    // HIT TRACKING METHODS

    /**
     * Get the current bind chance for a target.
     * Base 10% + 10% per previous hit.
     */
    public int getBindChanceForTarget(UUID targetUUID) {
        int hitCount = targetHitCounts.getOrDefault(targetUUID, 0);
        int baseChance =
            SettingsAccessor.getArcherArrowBindChanceBase();
        int perHitChance =
            SettingsAccessor.getArcherArrowBindChancePerHit();

        int chance = baseChance + (hitCount * perHitChance);
        return Math.min(chance, ARCHER_MAX_BIND_CHANCE);
    }

    /**
     * Record a hit on a target (increases future bind chance).
     */
    public void recordHitOnTarget(UUID targetUUID) {
        int currentHits = targetHitCounts.getOrDefault(targetUUID, 0);
        targetHitCounts.put(targetUUID, currentHits + 1);
        TiedUpMod.LOGGER.debug(
            "[EntityKidnapperArcher] {} hit target, new hit count: {}",
            this.getNpcName(),
            currentHits + 1
        );
    }

    /**
     * Clear hit counts for a specific target (called when target is captured).
     */
    public void clearHitsForTarget(UUID targetUUID) {
        targetHitCounts.remove(targetUUID);
    }

    /**
     * Clear all hit counts (called when archer changes target).
     */
    public void clearAllHitCounts() {
        targetHitCounts.clear();
    }

    // DISPLAY

    @Override
    public Component getDisplayName() {
        return Component.literal(this.getNpcName()).withStyle(
            Style.EMPTY.withColor(ARCHER_NAME_COLOR)
        );
    }

    // OVERRIDES

    /**
     * Archers take slightly longer to capture up close.
     */
    @Override
    public int getCaptureBindTime() {
        return ARCHER_CAPTURE_TIME_TICKS;
    }

    /**
     * Archers take slightly longer to gag.
     */
    @Override
    public int getCaptureGagTime() {
        return ARCHER_CAPTURE_TIME_TICKS;
    }

    /**
     * Archers use lower item probabilities - they rely on arrows.
     */
    @Override
    public KidnapperItemSelector.SelectionResult selectItemsForKidnapper() {
        return KidnapperItemSelector.selectForArcherKidnapper();
    }

    /**
     * Check if archer can use a bow (for animation layer).
     */
    public boolean canUseBow() {
        ItemStack mainHand = this.getMainHandItem();
        return mainHand.getItem() instanceof BowItem;
    }

    // BOW RESTORATION

    /**
     * Override clearHeldItems to restore the bow.
     * Archer should always have bow in hand when not actively capturing.
     */
    @Override
    public void clearHeldItems() {
        // Restore bow in main hand instead of clearing
        this.setItemSlot(
            net.minecraft.world.entity.EquipmentSlot.MAINHAND,
            new ItemStack(Items.BOW)
        );

        // Clear off hand
        this.setItemSlot(
            net.minecraft.world.entity.EquipmentSlot.OFFHAND,
            ItemStack.EMPTY
        );

        TiedUpMod.LOGGER.debug(
            "[EntityKidnapperArcher] {} restored bow after capture phase",
            this.getNpcName()
        );
    }

    // DIALOGUE SPEAKER (Archer-specific)

    @Override
    public SpeakerType getSpeakerType() {
        return SpeakerType.KIDNAPPER_ARCHER;
    }

    @Override
    public int getSpeakerMood() {
        // Archers are calm and precise
        if (this.isAiming()) {
            return 60; // Focused
        }
        return 55; // Calm
    }
}
