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

import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.items.ItemTaser;
import com.tiedup.remake.items.ModItems;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;

/**
 * AI Goal for camp kidnappers to hunt and kill monsters.
 *
 * Purpose: Protect captives from hostile mobs (zombies, skeletons, etc.)
 *
 * This goal:
 * 1. Only activates for camp kidnappers (with associated structure)
 * 2. Scans for nearby monsters
 * 3. Equips taser and attacks with stun effects
 * 4. Uses smaller scan radius when protecting a captive
 *
 * Combat bonuses vs monsters:
 * - 2x damage dealt (kidnappers are trained fighters)
 * - Taser stun effects (Slowness + Weakness)
 * - Creeper explosion cancelled for 1 second on hit
 * - 50% damage reduction (handled in EntityKidnapper.hurt())
 *
 * Priority: 1 (high - monster defense is important for captive safety)
 */
public class KidnapperHuntMonstersGoal extends Goal {

    private final EntityKidnapper kidnapper;
    private LivingEntity targetMonster;

    /** Scan radius when patrolling */
    private static final int SCAN_RADIUS = 16;

    /** Smaller radius when protecting captive (stay close) */
    private static final int SCAN_RADIUS_WITH_CAPTIVE = 10;

    /** Ticks between attacks (faster than normal) */
    private static final int ATTACK_COOLDOWN = 15;

    /** Damage multiplier against monsters */
    private static final float MONSTER_DAMAGE_MULTIPLIER = 2.0f;

    /** Current attack cooldown timer */
    private int attackCooldown = 0;

    public KidnapperHuntMonstersGoal(EntityKidnapper kidnapper) {
        this.kidnapper = kidnapper;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        // Only camp kidnappers hunt monsters
        if (kidnapper.getAssociatedStructure() == null) {
            return false;
        }

        // Don't hunt if chasing a player target
        if (kidnapper.getTarget() != null) {
            return false;
        }

        // Don't hunt if tied up
        if (kidnapper.isTiedUp()) {
            return false;
        }

        // Hunt monsters (with or without captive - protect captives from mobs!)
        this.targetMonster = findNearestMonster();
        return this.targetMonster != null;
    }

    @Override
    public boolean canContinueToUse() {
        if (this.targetMonster == null || !this.targetMonster.isAlive()) {
            return false;
        }

        // Stop if we now have a player target
        if (kidnapper.getTarget() != null) {
            return false;
        }

        // Stop if tied up
        if (kidnapper.isTiedUp()) {
            return false;
        }

        // Stop if monster is too far away (double the scan radius)
        int radius = kidnapper.hasCaptives()
            ? SCAN_RADIUS_WITH_CAPTIVE
            : SCAN_RADIUS;
        return (
            kidnapper.distanceToSqr(this.targetMonster) < radius * radius * 4
        );
    }

    @Override
    public void start() {
        this.attackCooldown = 0;

        // Equip taser
        this.kidnapper.setItemInHand(
            InteractionHand.MAIN_HAND,
            new ItemStack(ModItems.TASER.get())
        );
    }

    @Override
    public void stop() {
        this.targetMonster = null;
        this.kidnapper.getNavigation().stop();

        // Unequip taser
        this.kidnapper.setItemInHand(
            InteractionHand.MAIN_HAND,
            ItemStack.EMPTY
        );
    }

    @Override
    public void tick() {
        if (this.targetMonster == null) {
            return;
        }

        // Look at the monster
        this.kidnapper.getLookControl().setLookAt(this.targetMonster);

        double distSq = this.kidnapper.distanceToSqr(this.targetMonster);

        // Move closer if not in attack range
        if (distSq > 4.0) {
            this.kidnapper.getNavigation().moveTo(this.targetMonster, 1.2);
        } else {
            // In attack range - stop and attack with bonus damage
            this.kidnapper.getNavigation().stop();

            if (this.attackCooldown <= 0) {
                attackMonsterWithBonus(this.targetMonster);
                this.attackCooldown = ATTACK_COOLDOWN;
            }
        }

        // Decrement cooldown
        if (this.attackCooldown > 0) {
            this.attackCooldown--;
        }
    }

    /**
     * Attack a monster with taser - bonus damage and stun effects.
     * Kidnappers deal 2x damage to monsters and apply taser stun.
     * Creepers have their explosion cancelled for 1 second.
     *
     * @param target The monster to attack
     */
    private void attackMonsterWithBonus(LivingEntity target) {
        // Swing arm animation
        this.kidnapper.swing(InteractionHand.MAIN_HAND);

        // Get base attack damage and apply multiplier
        float baseDamage = (float) this.kidnapper.getAttributeValue(
            Attributes.ATTACK_DAMAGE
        );
        float bonusDamage = baseDamage * MONSTER_DAMAGE_MULTIPLIER;

        // Deal damage directly with bonus
        boolean damaged = target.hurt(
            this.kidnapper.damageSources().mobAttack(this.kidnapper),
            bonusDamage
        );

        // Apply taser effects if damage was dealt
        if (damaged) {
            ItemStack heldItem = this.kidnapper.getItemInHand(
                InteractionHand.MAIN_HAND
            );
            if (heldItem.getItem() instanceof ItemTaser taserItem) {
                taserItem.hurtEnemy(heldItem, target, this.kidnapper);
            }

            // Special: Cancel creeper explosion for 1 second
            if (target instanceof Creeper creeper) {
                // Set swell direction to -1 (shrinking) to cancel explosion
                creeper.setSwellDir(-1);
            }
        }
    }

    /**
     * Find the nearest monster within scan radius.
     *
     * @return The nearest monster, or null if none found
     */
    private LivingEntity findNearestMonster() {
        int radius = kidnapper.hasCaptives()
            ? SCAN_RADIUS_WITH_CAPTIVE
            : SCAN_RADIUS;
        AABB searchBox = this.kidnapper.getBoundingBox().inflate(radius);

        List<Monster> monsters = this.kidnapper.level().getEntitiesOfClass(
            Monster.class,
            searchBox,
            m ->
                m.isAlive() &&
                !m.isSpectator() &&
                Math.abs(m.getY() - this.kidnapper.getY()) < 5 &&
                this.kidnapper.getSensing().hasLineOfSight(m)
        );

        if (monsters.isEmpty()) {
            return null;
        }

        // Find nearest
        Monster nearest = null;
        double nearestDist = Double.MAX_VALUE;

        for (Monster m : monsters) {
            double dist = this.kidnapper.distanceToSqr(m);
            if (dist < nearestDist) {
                nearestDist = dist;
                nearest = m;
            }
        }

        return nearest;
    }
}
