package com.r3944realms.leashedplayer.content.entities;

import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry;
import com.r3944realms.leashedplayer.content.gamerules.Server.KeepLeashNotDropTime;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.ColorParticleOption;
import net.minecraft.core.particles.ParticleTypes;
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.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class LeashRopeArrow extends AbstractArrow  {
    private static final int EXPOSED_POTION_DECAY_TIME = 600;
    private static final int NO_EFFECT_COLOR = -1;
    private static final EntityDataAccessor<Integer> ID_EFFECT_COLOR = SynchedEntityData.defineId(LeashRopeArrow.class, EntityDataSerializers.INT);
    private static final byte EVENT_POTION_PUFF = 0;
    private static final int maxLifeTime = LeashPlayerCommonConfig.TheLeashArrowMaxLifeTime.get();
    protected LeashRopeArrow(EntityType<? extends AbstractArrow> entityType,Level pLevel) {
        super(entityType, pLevel);

    }
    private PotionContents getPotionContents() {
        return this.getPickupItemStackOrigin().getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
    }

    public void addEffect(MobEffectInstance pEffectInstance) {
        this.setPotionContents(this.getPotionContents().withEffectAdded(pEffectInstance));
    }

    private void setPotionContents(PotionContents pPotionContents) {
        this.getPickupItemStackOrigin().set(DataComponents.POTION_CONTENTS, pPotionContents);
        this.updateColor();
    }

    private void updateColor() {
        PotionContents potioncontents = this.getPotionContents();
        this.entityData.set(ID_EFFECT_COLOR, potioncontents.equals(PotionContents.EMPTY) ? -1 : potioncontents.getColor());
    }

    @Override
    protected void defineSynchedData(SynchedEntityData.@NotNull Builder pBuilder) {
        super.defineSynchedData(pBuilder);
        pBuilder.define(ID_EFFECT_COLOR, -1);
    }


    @Override
    protected void setPickupItemStack(@NotNull ItemStack pPickupItemStack) {
        super.setPickupItemStack(pPickupItemStack);
        this.updateColor();
    }

    public LeashRopeArrow(EntityType<? extends AbstractArrow> entityType, double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) {
        super(entityType, pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon);
        this.updateColor();
        if(serverPlayer != null && !level().isClientSide) {
            Entity leashDataEntity = PlayerLeashable.getLeashDataEntity(serverPlayer, (ServerLevel) level());
            if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                leashRopeArrow.setOwner(null);
            }
            ((PlayerLeashable)serverPlayer).setLeashedTo(this, true);
        }
    }

    public LeashRopeArrow(EntityType<? extends AbstractArrow> entityType, LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) {
        super(entityType, pOwner, pLevel, pPickupItemStack, pFiredFromWeapon);
        this.updateColor();
        if(pOwner instanceof PlayerLeashable lPlayer && !level().isClientSide) {
            Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) lPlayer, (ServerLevel) level());
            if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                leashRopeArrow.setOwner(null);
            }
            lPlayer.setLeashedTo(this, true);
        }
    }

    @Override
    protected @NotNull ItemStack getDefaultPickupItem() {
        return new ItemStack(ModItemRegister.LEASH_ROPE_ARROW.get());
    }

    @Override
    public void setOwner(@Nullable Entity pEntity) {
        boolean isNull = pEntity == null;
        this.ownerUUID = isNull ? null : pEntity.getUUID();
        this.cachedOwner = isNull ? null : pEntity;
        this.pickup = this.pickup == Pickup.CREATIVE_ONLY ? this.pickup : Pickup.DISALLOWED;
    }

    @Override
    protected boolean tryPickup(@NotNull Player pPlayer) {
        //时间1.40 禁止
        //时间2.240
    //          如果(非仅创造拾取)
            //      如果 (按Shift )
            //          如果(拥有者) -> 拾取到完整箭，取消绑定(super给父类处理)
            //          否则:时间仍为原需时间 ->不能获取完整的箭，重绑定（当前拥有者的Holder是否为本箭，“是”才重绑定）
            //      否则: 禁止
        //      否则:
            //      如果 (按Shift )
            //          如果(拥有者) -> 且拾取到完整箭，取消绑定
            //          否则:时间仍为原需时间 ->不能获取完整的箭，重绑定
            //      否则: 禁止
        //时间3
    //          如果(拥有者) -> 拾取到完整箭，取消绑定
    //          否则:不能获取完整的箭，重绑定

        if(life <= 40 ) {
           return false;
        }
        else {
            PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer;
            if(this.getOwner() == null) {//未有Owner始终可检
                return true;
            }
            if(life <= 240) {
                if(pPlayer.isShiftKeyDown()) {
                    Entity leashDataEntity =  this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner();
                    if(this.ownedBy(pPlayer)) {
                        this.pickup = Pickup.ALLOWED;
                        if(this.equals(leashDataEntity)) {
                            pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                            playerLeashable.dropLeash(true, false);
                        }
                    } else {
                        if(life >= 120) {
                            Entity owner = getOwner();
                            if( owner != null ) {
//                                    if(this.equals(leashDataEntity)) {
                                if(owner instanceof PlayerLeashable player) {
                                    player.setLeashedTo(pPlayer, true);
                                    pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                                } else if(owner instanceof Leashable leashable) {
                                    leashable.setLeashedTo(pPlayer, true);
                                    pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                                }
                                ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack());
                                level().addFreshEntity(itemEntity);
                                discard();
//                                    }
                            } else return true;
                        } else return false;
                    }
                } else return false;

            }
            else {
                Entity leashDataEntity =  this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner();
                if(this.ownedBy(pPlayer)) {
                    this.pickup = Pickup.ALLOWED;
                    if(this.equals(leashDataEntity)) {
                        pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                        playerLeashable.dropLeash(true, false);
                    }
                } else {
                    Entity owner = getOwner();
                    if(owner instanceof PlayerLeashable player) {
                        player.setLeashedTo(pPlayer, true);
                        pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    } else if(owner instanceof Leashable leashable) {
                        leashable.setLeashedTo(pPlayer, true);
                        pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    }
                    ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack());
                    level().addFreshEntity(itemEntity);
                    discard();

                }
            }
        }

        return super.tryPickup(pPlayer);
    }
    protected void hitOnEntityHandler(Entity pEntity) {
        if(pEntity instanceof LivingEntity pLiving) {
            super.doPostHurtEffects(pLiving);
            Entity entity = this.getEffectSource();
            PotionContents potioncontents = this.getPotionContents();
            if (potioncontents.potion().isPresent()) {
                for (MobEffectInstance mobeffectinstance : potioncontents.potion().get().value().getEffects()) {
                    pLiving.addEffect(
                            new MobEffectInstance(
                                    mobeffectinstance.getEffect(),
                                    Math.max(mobeffectinstance.mapDuration(p_268168_ -> p_268168_ / 8), 1),
                                    mobeffectinstance.getAmplifier(),
                                    mobeffectinstance.isAmbient(),
                                    mobeffectinstance.isVisible()
                            ),
                            entity
                    );
                }
            }
            for (MobEffectInstance effectInstance : potioncontents.customEffects()) {
                pLiving.addEffect(effectInstance, entity);
            }
        }
        //NOOP
    }
    protected ItemStack getOrginalItemStack() {
        return Items.ARROW.getDefaultInstance();
    }
    protected ItemStack getSelfItemStack() {
        return ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance();
    }

    @Override
    protected void tickDespawn() {
        this.life++;
        if (this.life >= maxLifeTime) {
            ItemEntity leash_rope_arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
            this.level().addFreshEntity(leash_rope_arrow);
            this.discard();
        }
    }
    @Override
    public void tick() {
        super.tick();
        if (this.level().isClientSide) {
            if (this.inGround) {
                if (this.inGroundTime % 5 == 0) {
                    this.makeParticle(1);
                }
            } else {
                this.makeParticle(2);
            }
        } else if (this.inGround && this.inGroundTime != 0 && !this.getPotionContents().equals(PotionContents.EMPTY) && this.inGroundTime >= 600) {
            this.level().broadcastEntityEvent(this, (byte)0);
            this.setPickupItemStack(new ItemStack(getSelfItemStack().getItem()));
        }
    }

    private void makeParticle(int pParticleAmount) {
        int i = this.getColor();
        if (i != -1 && pParticleAmount > 0) {
            for (int j = 0; j < pParticleAmount; j++) {
                this.level()
                        .addParticle(
                                ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, i),
                                this.getRandomX(0.5),
                                this.getRandomY(),
                                this.getRandomZ(0.5),
                                0.0,
                                0.0,
                                0.0
                        );
            }
        }
    }

    public int getColor() {
        return this.entityData.get(ID_EFFECT_COLOR);
    }


    @Override
    protected void onHitBlock(@NotNull BlockHitResult pResult) {
        if(!level().isClientSide) {
            if (getOwner() instanceof PlayerLeashable pL) {
                if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) {
                    Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
                    if(leashDataEntity != null) {
                        pL.dropLeash(true, !(leashDataEntity instanceof LeashRopeArrow));
                        if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                            leashDataEntity.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                            leashRopeArrow.setOwner(null);
                        }
                    }
                    Entity leashKnotFence = PlayerLeashable.createLeashKnotFence((ServerLevel) this.level(), pResult.getBlockPos());
                    ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
                    pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
                    pL.setLeashedTo(leashKnotFence, true);
                    leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
                    this.level().addFreshEntity(arrow);
                    discard();
                }
            } else if(getOwner() instanceof Leashable L) {
                if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) {
                    Entity leashDataEntity = this.getOwner();
                    if(leashDataEntity != null) {
                        leashDataEntity.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                        L.dropLeash(true, false);
                        if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                            leashRopeArrow.setOwner(null);
                        }
                    }
                    Entity leashKnotFence = LeashFenceKnotEntity.getOrCreateKnot(this.level(), pResult.getBlockPos());
                    L.setLeashedTo(leashKnotFence, true);
                    leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
                    this.level().addFreshEntity(arrow);
                    discard();
                }
            }
        }
        super.onHitBlock(pResult);

    }

    @Override
    protected void onHitEntity(@NotNull EntityHitResult pResult) {
        if(!level().isClientSide()){
            Entity entity = pResult.getEntity();
            hitOnEntityHandler(entity);
            if(this.getOwner() instanceof LivingEntity livingEntity ) {
                MobEffectInstance effect = livingEntity.getEffect(ModEffectRegister.NO_LEASH_EFFECT);
                if(effect != null && effect.getDuration() != 0) {
                    livingEntity.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                    this.setOwner(null);
                }
            }
            if(entity instanceof LivingEntity livingEntity) {
                if(livingEntity.equals(this.getOwner())) return;
                if(this.getOwner() == null && livingEntity instanceof PlayerLeashable pL) { //发射器发出或命令生成
                    setOwner(livingEntity);
                    Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
                    if(leashDataEntity != null) {
                        pL.dropLeash(true, !(leashDataEntity instanceof LeashRopeArrow));
                        if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                            leashRopeArrow.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                            leashRopeArrow.setOwner(null);
                        }
                    }
                    ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
                    pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
                    livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    pL.setLeashedTo(this, true);
                    return;
                } else if (this.getOwner() instanceof PlayerLeashable pL) {
                    Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
                    if(leashDataEntity != null) {
                        pL.dropLeash(true, !(leashDataEntity instanceof LeashRopeArrow));
                        if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                            leashRopeArrow.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                            leashRopeArrow.setOwner(null);
                        }
                    }
                    ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
                    ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
                    pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
                    livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    pL.setLeashedTo(pResult.getEntity(), true);
                    this.level().addFreshEntity(arrow);
                    discard();
                } else {
                    if(entity instanceof Leashable leashable) {
                        if (getOwner() == null) {
                            Entity leashDataEntity = leashable.getLeashHolder();
                            if (leashDataEntity != null) {
                                leashable.dropLeash(true, !(leashDataEntity instanceof LeashRopeArrow));
                                if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                                    leashRopeArrow.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                                    leashRopeArrow.setOwner(null);
                                }
                            }
                            livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                            leashable.setLeashedTo(this, true);
                            this.setOwner(entity);
                            return;
                        }
                    }
                    if(entity instanceof LivingEntity living) {
                        if(this.getOwner() != null && this.getOwner()instanceof Leashable leashable) {
                            livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                            leashable.setLeashedTo(living, true);
                            ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
                            this.level().addFreshEntity(arrow);
                            discard();
                            return;
                        }
                    }
                    ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance());
                    this.level().addFreshEntity(lead);
                }
            }
            else if (entity instanceof LeashFenceKnotEntity leashKnotFence) {
                if (getOwner() instanceof PlayerLeashable pL) {
                    Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
                    if(leashDataEntity != null) {
                        pL.dropLeash(true, true);
                        if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
                            leashRopeArrow.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
                            leashRopeArrow.setOwner(null);
                        }
                    }
                    ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
                    ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
                    pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
                    leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
                    pL.setLeashedTo(leashKnotFence, true);
                    this.level().addFreshEntity(arrow);
                    discard();
                    return;
                }
            } else {
                ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance());
                this.level().addFreshEntity(lead);
            }
        }
        if(!level().isClientSide()) super.onHitEntity(pResult);
    }
    /**
     * Handles an entity event received from a {@link net.minecraft.network.protocol.game.ClientboundEntityEventPacket}.
     */
    @Override
    public void handleEntityEvent(byte pId) {
        if (pId == 0) {
            int i = this.getColor();
            if (i != -1) {
                float f = (float)(i >> 16 & 0xFF) / 255.0F;
                float f1 = (float)(i >> 8 & 0xFF) / 255.0F;
                float f2 = (float)(i & 0xFF) / 255.0F;

                for (int j = 0; j < 20; j++) {
                    this.level()
                            .addParticle(
                                    ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, f, f1, f2),
                                    this.getRandomX(0.5),
                                    this.getRandomY(),
                                    this.getRandomZ(0.5),
                                    0.0,
                                    0.0,
                                    0.0
                            );
                }
            }
        } else {
            super.handleEntityEvent(pId);
        }
    }

}
