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

import com.tiedup.remake.entities.EntityMaster;
import com.tiedup.remake.items.ItemTaser;
import com.tiedup.remake.items.ModItems;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.server.level.ServerPlayer;
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 Masters to hunt and kill monsters near their pet.
 *
 * Purpose: Protect the pet player from hostile mobs
 *
 * This goal:
 * 1. Only activates when master has a pet
 * 2. Scans for monsters near the pet (not the master)
 * 3. Equips taser and attacks with stun effects
 * 4. Returns to pet after killing monster
 *
 * Combat bonuses vs monsters:
 * - 2x damage dealt (masters are elite fighters)
 * - Taser stun effects (Slowness + Weakness)
 * - Creeper explosion cancelled on hit
 *
 * Priority: 1 (high - pet protection is paramount)
 */
public class MasterHuntMonstersGoal extends Goal {

    private final EntityMaster master;
    private LivingEntity targetMonster;

    /** Scan radius around pet */
    private static final int SCAN_RADIUS = 12;

    /** Ticks between attacks */
    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 MasterHuntMonstersGoal(EntityMaster master) {
        this.master = master;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        // Must have a pet to protect
        if (!master.hasPet()) {
            return false;
        }

        ServerPlayer pet = master.getPetPlayer();
        if (pet == null || !pet.isAlive()) {
            return false;
        }

        // Find monsters near the pet (not the master)
        this.targetMonster = findNearestMonsterNearPet(pet);
        return this.targetMonster != null;
    }

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

        // Stop if pet is gone
        if (!master.hasPet()) {
            return false;
        }

        // Stop if monster is too far from master (chase limit)
        return master.distanceToSqr(this.targetMonster) < 400; // 20 blocks
    }

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

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

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

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

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

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

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

        // Move closer if not in attack range
        if (distSq > 4.0) {
            this.master.getNavigation().moveTo(this.targetMonster, 1.3);
        } else {
            // In attack range - stop and attack
            this.master.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.
     * Masters deal 2x damage to monsters and apply taser stun.
     * Creepers have their explosion cancelled.
     *
     * @param target The monster to attack
     */
    private void attackMonsterWithBonus(LivingEntity target) {
        // Swing arm animation
        this.master.swing(InteractionHand.MAIN_HAND);

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

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

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

            // Special: Cancel creeper explosion
            if (target instanceof Creeper creeper) {
                creeper.setSwellDir(-1);
            }
        }
    }

    /**
     * Find the nearest monster within scan radius of the pet.
     *
     * @param pet The pet player to protect
     * @return The nearest monster, or null if none found
     */
    private LivingEntity findNearestMonsterNearPet(ServerPlayer pet) {
        AABB searchBox = pet.getBoundingBox().inflate(SCAN_RADIUS);

        List<Monster> monsters = this.master.level().getEntitiesOfClass(
            Monster.class,
            searchBox,
            m -> m.isAlive() && !m.isSpectator()
        );

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

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

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

        return nearest;
    }
}
