package com.Polarice3.Goety.common.entities.ally.golem;

import com.Polarice3.Goety.client.particles.ModParticleTypes;
import com.Polarice3.Goety.common.entities.neutral.Owned;
import com.Polarice3.Goety.common.network.ModNetwork;
import com.Polarice3.Goety.common.network.server.SRCGlowPacket;
import com.Polarice3.Goety.config.AttributesConfig;
import com.Polarice3.Goety.config.MobsConfig;
import com.Polarice3.Goety.init.ModSounds;
import com.Polarice3.Goety.init.ModTags;
import com.Polarice3.Goety.utils.ColorUtil;
import com.Polarice3.Goety.utils.EntityFinder;
import com.Polarice3.Goety.utils.MathHelper;
import com.Polarice3.Goety.utils.MobUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.ai.util.LandRandomPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.Tags;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

public class RedstoneCube extends AbstractGolemServant{
    private static final EntityDataAccessor<Integer> ANIM_STATE = SynchedEntityData.defineId(RedstoneCube.class, EntityDataSerializers.INT);
    protected static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(RedstoneCube.class, EntityDataSerializers.BYTE);
    public static String IDLE = "idle";
    public static String ATTACK = "attack";
    public static String WALK = "walk";
    public int attackTick;
    public float minorGlow;
    public float bigGlow;
    public float glowAmount = 0.01F;
    public Vec3 detectPos;
    public AnimationState idleAnimationState = new AnimationState();
    public AnimationState walkAnimationState = new AnimationState();
    public AnimationState attackAnimationState = new AnimationState();

    public RedstoneCube(EntityType<? extends Owned> type, Level worldIn) {
        super(type, worldIn);
        this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F);
        this.setPathfindingMalus(BlockPathTypes.UNPASSABLE_RAIL, 0.0F);
        this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
    }

    protected void registerGoals() {
        super.registerGoals();
        this.goalSelector.addGoal(8, new SeekBlockGoal(this));
        this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
        this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
    }

    @SuppressWarnings("removal")
    public static AttributeSupplier.Builder setCustomAttributes(){
        return Mob.createMobAttributes()
                .add(Attributes.MAX_HEALTH, AttributesConfig.RedstoneCubeHealth.get())
                .add(Attributes.MOVEMENT_SPEED, 0.3D)
                .add(Attributes.ATTACK_DAMAGE, AttributesConfig.RedstoneCubeDamage.get())
                .add(Attributes.ARMOR, AttributesConfig.RedstoneCubeArmor.get())
                .add(ForgeMod.STEP_HEIGHT_ADDITION.get(), 1.0D)
                .add(Attributes.KNOCKBACK_RESISTANCE, 0.75D)
                .add(Attributes.FOLLOW_RANGE, AttributesConfig.RedstoneCubeFollowRange.get());
    }

    public void setConfigurableAttributes(){
        MobUtil.setBaseAttributes(this.getAttribute(Attributes.MAX_HEALTH), AttributesConfig.RedstoneCubeHealth.get());
        MobUtil.setBaseAttributes(this.getAttribute(Attributes.ARMOR), AttributesConfig.RedstoneCubeArmor.get());
        MobUtil.setBaseAttributes(this.getAttribute(Attributes.ATTACK_DAMAGE), AttributesConfig.RedstoneCubeDamage.get());
        MobUtil.setBaseAttributes(this.getAttribute(Attributes.FOLLOW_RANGE), AttributesConfig.RedstoneCubeFollowRange.get());
    }

    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.define(DATA_FLAGS_ID, (byte)0);
        this.entityData.define(ANIM_STATE, 0);
    }

    @Nullable
    @Override
    protected SoundEvent getHurtSound(DamageSource p_21239_) {
        return ModSounds.REDSTONE_CUBE_HURT.get();
    }

    @Nullable
    @Override
    protected SoundEvent getDeathSound() {
        return ModSounds.REDSTONE_CUBE_HURT.get();
    }

    @Override
    protected void playStepSound(BlockPos p_20135_, BlockState p_20136_) {
        this.playSound(ModSounds.REDSTONE_CUBE_WALK.get(), 0.5F, 1.0F);
    }

    @Override
    public boolean canBeCollidedWith() {
        return false;
    }

    @Override
    public boolean isPushable() {
        return this.isAlive() && !this.isSpectator() && !this.onClimbable();
    }

    @Override
    public boolean doHurtTarget(Entity entityIn) {
        if (entityIn instanceof Mob mob){
            if (mob.getTarget() != this){
                mob.setTarget(this);
            }
        }

        return super.doHurtTarget(entityIn);
    }

    @Nullable
    public SpawnGroupData finalizeSpawn(ServerLevelAccessor pLevel, DifficultyInstance pDifficulty, MobSpawnType pReason, @javax.annotation.Nullable SpawnGroupData pSpawnData, @javax.annotation.Nullable CompoundTag pDataTag) {
        pSpawnData = super.finalizeSpawn(pLevel, pDifficulty, pReason, pSpawnData, pDataTag);
        if (pReason == MobSpawnType.MOB_SUMMONED){
            if (pLevel instanceof ServerLevel serverLevel){
                for (int i = 0; i < 8; ++i) {
                    double d0 = (double) this.getX() + this.random.nextDouble();
                    double d1 = (double) this.getY() + 1.0D;
                    double d2 = (double) this.getZ() + this.random.nextDouble();
                    serverLevel.sendParticles(ParticleTypes.LAVA, d0, d1, d2, 0, 0.0D, 0.5D, 0.0D, 0.5F);
                }
                this.playSound(ModSounds.REDSTONE_CUBE_BURST.get(), 0.65F, 0.7F + Math.min(this.random.nextFloat(), 0.5F));
            }
        }
        return pSpawnData;
    }

    public boolean canAnimateMove(){
        return super.canAnimateMove() && this.getCurrentAnimation() == this.getAnimationState(WALK);
    }

    public void setAnimationState(String input) {
        this.setAnimationState(this.getAnimationState(input));
    }

    public void setAnimationState(int id) {
        this.entityData.set(ANIM_STATE, id);
    }

    public int getAnimationState(String animation) {
        if (Objects.equals(animation, "idle")){
            return 1;
        } else if (Objects.equals(animation, "walk")){
            return 2;
        } else if (Objects.equals(animation, "attack")){
            return 3;
        } else {
            return 0;
        }
    }

    public List<AnimationState> getAllAnimations(){
        List<AnimationState> animationStates = new ArrayList<>();
        animationStates.add(this.idleAnimationState);
        animationStates.add(this.walkAnimationState);
        animationStates.add(this.attackAnimationState);
        return animationStates;
    }

    public void stopMostAnimation(AnimationState exception){
        for (AnimationState state : this.getAllAnimations()){
            if (state != exception){
                state.stop();
            }
        }
    }

    public int getCurrentAnimation(){
        return this.entityData.get(ANIM_STATE);
    }

    public void onSyncedDataUpdated(EntityDataAccessor<?> accessor) {
        if (ANIM_STATE.equals(accessor)) {
            if (this.level.isClientSide){
                switch (this.entityData.get(ANIM_STATE)){
                    case 0:
                        break;
                    case 1:
                        this.idleAnimationState.startIfStopped(this.tickCount);
                        this.stopMostAnimation(this.idleAnimationState);
                        break;
                    case 2:
                        this.walkAnimationState.startIfStopped(this.tickCount);
                        this.stopMostAnimation(this.walkAnimationState);
                        break;
                    case 3:
                        this.attackAnimationState.start(this.tickCount);
                        this.stopMostAnimation(this.attackAnimationState);
                        break;
                }
            }
        }
    }

    private boolean getFlag(int mask) {
        int i = this.entityData.get(DATA_FLAGS_ID);
        return (i & mask) != 0;
    }

    private void setFlag(int mask, boolean value) {
        int i = this.entityData.get(DATA_FLAGS_ID);
        if (value) {
            i = i | mask;
        } else {
            i = i & ~mask;
        }

        this.entityData.set(DATA_FLAGS_ID, (byte)(i & 255));
    }

    public boolean isMeleeAttacking() {
        return this.getFlag(1);
    }

    public void setMeleeAttacking(boolean attack) {
        this.setFlag(1, attack);
        this.attackTick = 0;
        this.level.broadcastEntityEvent(this, (byte) 5);
    }

    @Override
    protected boolean isAffectedByFluids() {
        return false;
    }

    @Override
    public void tick() {
        super.tick();
        if (this.isAlive()) {
            if (!this.level.isClientSide) {
                if (this.isMeleeAttacking()) {
                    this.getNavigation().stop();
                    ++this.attackTick;
                    if (this.attackTick >= MathHelper.secondsToTicks(1)){
                        this.setMeleeAttacking(false);
                        this.setAnimationState(IDLE);
                    } else if (this.getCurrentAnimation() != this.getAnimationState(ATTACK)){
                        this.setAnimationState(ATTACK);
                    }
                } else {
                    if (this.isMoving()) {
                        if (this.level instanceof ServerLevel serverLevel){
                            if (this.onGround() && this.tickCount % 5 == 0) {
                                ColorUtil colorUtil = new ColorUtil(16711680);
                                serverLevel.sendParticles(ModParticleTypes.REDSTONE_DEBRIS.get(), this.getX(), this.getY() + 0.1F, this.getZ(), 0, colorUtil.red, colorUtil.green, colorUtil.blue, 1.0F);
                            }
                        }
                        this.setAnimationState(WALK);
                    } else {
                        this.setAnimationState(IDLE);
                    }
                }
                if (this.getTarget() != null){
                    if (this.getTarget().isAlive() && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(this.getTarget())){
                        if (this.hasLineOfSight(this.getTarget())) {
                            if (this.targetClose(this.getTarget(), this.distanceToSqr(this.getTarget()))) {
                                if (!this.isMeleeAttacking()) {
                                    this.setMeleeAttacking(true);
                                }
                                if (this.attackTick == 8) {
                                    this.doHurtTarget(this.getTarget());
                                    this.playSound(ModSounds.REDSTONE_CUBE_ATTACK.get(), 0.6F, 1.0F);
                                }
                            } else {
                                this.getNavigation().moveTo(this.getTarget(), 1.25F);
                            }
                        }
                    }
                    if (!EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(this.getTarget())) {
                        this.setTarget(null);
                    }
                }
                ItemStack itemStack = this.getMainHandItem();
                if (!itemStack.isEmpty() && MobsConfig.RedstoneCubeBlockFind.get()){
                    Block block = Block.byItem(itemStack.getItem());
                    if (this.tickCount % 20 == 0) {
                        List<BlockPos> blockPosList = new ArrayList<>();
                        this.detectPos = null;
                        for (int i = -16; i <= 16; ++i){
                            for (int j = -16; j <= 16; ++j){
                                for (int k = -16; k <= 16; ++k){
                                    BlockPos blockPos = this.blockPosition().offset(i, j, k);
                                    if (this.level.getBlockState(blockPos).getBlock() == block){
                                        blockPosList.add(blockPos);
                                    }
                                }
                            }
                        }
                        if (!blockPosList.isEmpty()){
                            blockPosList.sort(Comparator.comparingDouble(blockPos -> this.distanceToSqr(Vec3.atCenterOf(blockPos))));
                            if (blockPosList.stream().findFirst().isPresent()){
                                BlockPos blockPos = blockPosList.stream().findFirst().get();
                                float distance = 1.0F - ((float) this.distanceToSqr(Vec3.atCenterOf(blockPos)) / Mth.square(16.0F));
                                this.bigGlow = distance;
                                this.playSound(ModSounds.TOCK.get(), distance, 1.5F);
                                this.detectPos = Vec3.atCenterOf(blockPos);
                            }
                        }
                    }
                } else {
                    this.detectPos = null;
                }
                this.bigGlow = Math.max(this.bigGlow - 0.01F, 0.0F);
                ModNetwork.sentToTrackingEntityAndPlayer(this, new SRCGlowPacket(this.getId(), this.bigGlow));
            } else {
                this.glow();
            }
        }
    }

    private void glow() {
        this.minorGlow = Mth.clamp(this.minorGlow + this.glowAmount, 0, 1);
        if (this.minorGlow == 0 || this.minorGlow == 1) {
            this.glowAmount *= -1;
        }
    }

    @Override
    public double getAttackReachSqr(LivingEntity enemy) {
        return this.getBbWidth() * 2.0F * this.getBbWidth() * 2.0F + enemy.getBbWidth();
    }

    @Override
    public void handleEntityEvent(byte p_21375_) {
        if (p_21375_ == 5){
            this.attackTick = 0;
        } else if (p_21375_ == 6){
            this.setAggressive(true);
        } else if (p_21375_ == 7){
            this.setAggressive(false);
        } else {
            super.handleEntityEvent(p_21375_);
        }
    }

    public InteractionResult mobInteract(Player pPlayer, InteractionHand pHand) {
        if (!this.level.isClientSide) {
            ItemStack itemstack = pPlayer.getItemInHand(pHand);
            Item item = itemstack.getItem();
            if (this.getTrueOwner() != null && pPlayer == this.getTrueOwner()) {
                if ((pPlayer.isCrouching() || pPlayer.isShiftKeyDown())
                        && pHand == InteractionHand.MAIN_HAND
                        && MobsConfig.RedstoneCubeBlockFind.get()){
                    if (item instanceof BlockItem blockItem
                            && blockItem.getBlock().defaultBlockState().is(ModTags.Blocks.REDSTONE_CUBE_DETECT)
                            && !blockItem.getBlock().defaultBlockState().is(ModTags.Blocks.REDSTONE_CUBE_EXEMPT)
                            && this.getMainHandItem().getItem() != item){
                        this.playSound(SoundEvents.ARROW_HIT_PLAYER, 1.0F, 1.25F);
                        this.setItemSlot(EquipmentSlot.MAINHAND, itemstack.copyWithCount(1));
                        this.setDropChance(EquipmentSlot.MAINHAND, 0.0F);
                        EntityFinder.sendEntityUpdatePacket(pPlayer, this);
                        return InteractionResult.SUCCESS;
                    } else if (itemstack.isEmpty() && !this.getMainHandItem().isEmpty()){
                        this.playSound(SoundEvents.ARROW_HIT_PLAYER, 1.0F, 1.25F);
                        this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
                        EntityFinder.sendEntityUpdatePacket(pPlayer, this);
                        return InteractionResult.SUCCESS;
                    }
                } else if ((itemstack.is(Tags.Items.STORAGE_BLOCKS_REDSTONE) || itemstack.is(Tags.Items.DUSTS_REDSTONE)) && this.getHealth() < this.getMaxHealth()) {
                    if (!pPlayer.getAbilities().instabuild) {
                        itemstack.shrink(1);
                    }
                    if (itemstack.is(Tags.Items.STORAGE_BLOCKS_REDSTONE)){
                        this.heal(this.getMaxHealth());
                        this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0F, 1.25F);
                    } else {
                        this.heal(this.getMaxHealth() / 4.0F);
                        this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 0.25F, 1.0F);
                    }
                    if (this.level instanceof ServerLevel serverLevel) {
                        for (int i = 0; i < 7; ++i) {
                            double d0 = serverLevel.random.nextGaussian() * 0.02D;
                            double d1 = serverLevel.random.nextGaussian() * 0.02D;
                            double d2 = serverLevel.random.nextGaussian() * 0.02D;
                            serverLevel.sendParticles(DustParticleOptions.REDSTONE, this.getRandomX(1.0D), this.getRandomY(), this.getRandomZ(1.0D), 0, d0, d1, d2, 0.5F);
                        }
                    }
                    return InteractionResult.SUCCESS;
                }
            }
        }
        return InteractionResult.PASS;
    }

    public static class SeekBlockGoal extends WanderGoal<RedstoneCube> {
        private final RedstoneCube redstoneCube;

        public SeekBlockGoal(RedstoneCube cube){
            super(cube, 1.0D, 60, 10);
            this.redstoneCube = cube;
        }

        @Nullable
        protected Vec3 getPosition() {
            if (this.redstoneCube.detectPos != null) {
                Vec3 vec3 = LandRandomPos.getPosTowards(this.redstoneCube, 10, 7, this.redstoneCube.detectPos);
                return vec3 == null ? super.getPosition() : vec3;
            } else {
                return super.getPosition();
            }
        }

    }
}
