package net.minecraft.world.entity;

import java.util.Objects;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.core.UUIDUtil;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;

public interface NeutralMob {
    String TAG_ANGER_TIME = "AngerTime";
    String TAG_ANGRY_AT = "AngryAt";

    int getRemainingPersistentAngerTime();

    void setRemainingPersistentAngerTime(int remainingPersistentAngerTime);

    @Nullable
    UUID getPersistentAngerTarget();

    void setPersistentAngerTarget(@Nullable UUID persistentAngerTarget);

    void startPersistentAngerTimer();

    default void addPersistentAngerSaveData(ValueOutput output) {
        output.putInt("AngerTime", this.getRemainingPersistentAngerTime());
        output.storeNullable("AngryAt", UUIDUtil.CODEC, this.getPersistentAngerTarget());
    }

    default void readPersistentAngerSaveData(Level level, ValueInput input) {
        this.setRemainingPersistentAngerTime(input.getIntOr("AngerTime", 0));
        if (level instanceof ServerLevel serverLevel) {
            UUID uuid = input.read("AngryAt", UUIDUtil.CODEC).orElse(null);
            this.setPersistentAngerTarget(uuid);
            // Paper - Prevent entity loading causing async lookups; Moved diff to separate method
            // If this entity already survived its first tick, e.g. is loaded and ticked in sync, actively
            // tick the initial persistent anger.
            // If not, let the first tick on the baseTick call the method later down the line.
            if (this instanceof Entity entity && !entity.firstTick) this.tickInitialPersistentAnger(level);
        }
    }

    default void updatePersistentAnger(ServerLevel serverLevel, boolean updateAnger) {
        LivingEntity target = this.getTarget();
        UUID persistentAngerTarget = this.getPersistentAngerTarget();
        if ((target == null || target.isDeadOrDying()) && persistentAngerTarget != null && serverLevel.getEntity(persistentAngerTarget) instanceof Mob) {
            this.stopBeingAngry();
        } else {
            if (target != null && !Objects.equals(persistentAngerTarget, target.getUUID())) {
                this.setPersistentAngerTarget(target.getUUID());
                this.startPersistentAngerTimer();
            }

            if (this.getRemainingPersistentAngerTime() > 0 && (target == null || target.getType() != EntityType.PLAYER || !updateAnger)) {
                this.setRemainingPersistentAngerTime(this.getRemainingPersistentAngerTime() - 1);
                if (this.getRemainingPersistentAngerTime() == 0) {
                    this.stopBeingAngry();
                }
            }
        }
    }

    default boolean isAngryAt(LivingEntity entity, ServerLevel level) {
        return this.canAttack(entity)
            && (entity.getType() == EntityType.PLAYER && this.isAngryAtAllPlayers(level) || entity.getUUID().equals(this.getPersistentAngerTarget()));
    }

    default boolean isAngryAtAllPlayers(ServerLevel level) {
        return level.getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER) && this.isAngry() && this.getPersistentAngerTarget() == null;
    }

    default boolean isAngry() {
        return this.getRemainingPersistentAngerTime() > 0;
    }

    default void playerDied(ServerLevel level, Player player) {
        if (level.getGameRules().getBoolean(GameRules.RULE_FORGIVE_DEAD_PLAYERS)) {
            if (player.getUUID().equals(this.getPersistentAngerTarget())) {
                this.stopBeingAngry();
            }
        }
    }

    default void forgetCurrentTargetAndRefreshUniversalAnger() {
        this.stopBeingAngry();
        this.startPersistentAngerTimer();
    }

    default void stopBeingAngry() {
        this.setLastHurtByMob(null);
        this.setPersistentAngerTarget(null);
        this.setTarget(null, org.bukkit.event.entity.EntityTargetEvent.TargetReason.FORGOT_TARGET); // CraftBukkit
        this.setRemainingPersistentAngerTime(0);
    }

    @Nullable
    LivingEntity getLastHurtByMob();

    void setLastHurtByMob(@Nullable LivingEntity livingEntity);

    void setTarget(@Nullable LivingEntity livingEntity);

    boolean setTarget(@Nullable LivingEntity livingEntity, @Nullable org.bukkit.event.entity.EntityTargetEvent.TargetReason reason); // CraftBukkit

    boolean canAttack(LivingEntity entity);

    @Nullable
    LivingEntity getTarget();

    // Paper start - Prevent entity loading causing async lookups
    // Update last hurt when ticking
    default void tickInitialPersistentAnger(Level level) {
        UUID uuid = getPersistentAngerTarget();
        if (uuid == null) {
            return;
        }

        if (level.getEntity(uuid) instanceof net.minecraft.world.entity.LivingEntity livingEntity) {
            this.setTarget(livingEntity, null);
        }
    }
    // Paper end - Prevent entity loading causing async lookups
}
