package com.leisuretimedock.blasttravelreborn.content.entity;

import com.leisuretimedock.blasttravelreborn.BlastTravelReborn;
import com.leisuretimedock.blasttravelreborn.api.events.CannonFireEvent;
import com.leisuretimedock.blasttravelreborn.content.BTRParticleTypes;
import com.leisuretimedock.blasttravelreborn.content.entity.cannon.CannonBehavior;
import com.leisuretimedock.blasttravelreborn.content.entity.cannon.ConcretePowderCannonBehavior;
import com.leisuretimedock.blasttravelreborn.content.entity.cannon.EntityCannonBehavior;
import com.leisuretimedock.blasttravelreborn.content.item.BTRItems;
import com.leisuretimedock.blasttravelreborn.content.menu.CannonContainerMenu;
import com.leisuretimedock.blasttravelreborn.network.BTRNetwork;
import com.leisuretimedock.blasttravelreborn.network.toClient.FireCannonPayload;
import com.leisuretimedock.blasttravelreborn.network.toServer.RequestFirePayload;
import com.leisuretimedock.blasttravelreborn.util.PlayerEntityDuck;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
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.sounds.SoundSource;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.Mth;
import net.minecraft.world.*;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.PickaxeItem;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.network.NetworkDirection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@SuppressWarnings("unused")
public class CannonEntity extends Entity {
	public static final Component UI_TITLE = Component.translatable("container.blasttravelreborn.cannon_container_menu");
	public static final Component NO_GUNPOWDER_DIALOG = Component.translatable("dialog.blasttravelreborn.no_gunpowder").withStyle(ChatFormatting.RED);
	public static final Component FULL_CANNON_DIALOG = Component.translatable("dialog.blasttravelreborn.full_cannon").withStyle(ChatFormatting.RED);
	public static final String ADD_GUNPOWDER_DIALOG = "dialog.blasttravelreborn.add_gunpowder_item";
	public static final String ADD_BEHAVIOR_DIALOG = "dialog.blasttravelreborn.add_behavior_item";
	public static final String ADD_LOCK_CHAIN_DIALOG = "dialog.blasttravelreborn.add_lock_chain_item";
	public static final String REMOVE_LOCK_CHAIN_DIALOG = "dialog.blasttravelreborn.remove_lock_chain_item";
	public static final Component FIRE = Component.translatable("dialog.blasttravelreborn.fire").withStyle(ChatFormatting.RED);

	public static final CannonBehavior NONE = new CannonBehavior(Items.AIR, stack -> false).register();
	public static final CannonBehavior GOLDEN = new CannonBehavior(Items.GOLD_BLOCK, BlastTravelReborn.id("textures/entity/cannon/golden.png")).register();
	public static final CannonBehavior MOSSY = new CannonBehavior(Items.MOSS_BLOCK, BlastTravelReborn.id("textures/entity/cannon/mossy.png")).register();
	public static final CannonBehavior LAZULI = new CannonBehavior(Items.LAPIS_BLOCK, BlastTravelReborn.id("textures/entity/cannon/lazuli.png")).register();
	public static final CannonBehavior AMETHYST = new CannonBehavior(Items.AMETHYST_BLOCK, BlastTravelReborn.id("textures/entity/cannon/amethyst.png")).register();
	public static final CannonBehavior TNT = new EntityCannonBehavior(Items.TNT, BlastTravelReborn.id("textures/entity/cannon/tnt.png"), EntityCannonBehavior::tntFactory).register();
	public static final CannonBehavior ANVIL = new EntityCannonBehavior(Items.ANVIL, s -> s.is(ItemTags.ANVIL), BlastTravelReborn.id("textures/entity/cannon/anvil.png"), EntityCannonBehavior::fallingBlockFactory).register();
	public static final CannonBehavior POWDER = new ConcretePowderCannonBehavior().register();
	//Register Your Mod Item with Cannon Behavior Like this
	public static final CannonBehavior SAND = new EntityCannonBehavior(Items.SAND, BlastTravelReborn.id(/*your mod_id*/BlastTravelReborn.MOD_ID,/*the cannon texture path*/"textures/entity/cannon/sand.png"), EntityCannonBehavior::fallingBlockFactory).register();
	public static final CannonBehavior RED_SAND = new EntityCannonBehavior(Items.RED_SAND, BlastTravelReborn.id("textures/entity/cannon/red_sand.png"), EntityCannonBehavior::fallingBlockFactory).register();

	public static final EntityDataAccessor<Integer> BEHAVIOR = SynchedEntityData.defineId(CannonEntity.class, EntityDataSerializers.INT);
	public static final EntityDataAccessor<Boolean> CHAINED = SynchedEntityData.defineId(CannonEntity.class, EntityDataSerializers.BOOLEAN);
	public static final EntityDataAccessor<ItemStack> BEHAVIOR_STACK = SynchedEntityData.defineId(CannonEntity.class, EntityDataSerializers.ITEM_STACK);

	public static final int MAX_ANIMATION = 12;

	private boolean chained;
	private boolean firing;
	private boolean powered;
	private boolean alwaysModifiable;

	private int animation = 0;

	private double targetX;
	private double targetY;
	private double targetZ;
	private int targetTicks;

	private final SimpleContainer inventory = new SimpleContainer(3) {
		@Override
		public void setItem(int slot, @NotNull ItemStack stack) {
			super.setItem(slot, stack);
			CannonEntity.this.updateStateFromInventory();
		}
	};

	public CannonEntity(EntityType<?> type, Level world) {
		super(type, world);
	}

	public CannonEntity(Level world) {
		this(BTREntityTypes.CANNON_ENTITY.get(), world);
	}

	@Override
	public void tick() {
		if (this.firing && !this.isVehicle()) {
			this.firing = false;
		}

		super.tick();

		if (!this.chained && this.getFirstPassenger() instanceof Player player) {
			this.setYRot(player.getYHeadRot());
			this.setXRot(player.getXRot());
		}

		if (this.entityData.get(CHAINED) != this.chained) {
			if (!level().isClientSide()) {
				this.entityData.set(CHAINED, this.chained);
			} else {
				setChained(this.entityData.get(CHAINED));
			}
		}

		if (this.animation > 0) {
			this.animation--;
		}

		if (this.level().isClientSide()) {
			if (this.hasFuse()) {
				var pos = this.position().add(0, 0.75, 0).add(this.calculateViewVector(this.getXRot() - 90, this.getYRot()).scale(0.75));
				Minecraft.getInstance().particleEngine.createParticle(ParticleTypes.SMOKE,
						pos.x, pos.y, pos.z, 0, 0, 0);
			}

			this.positionTrackTick();
		} else {
			boolean hasPower = this.level().getBestNeighborSignal(this.blockPosition()) > 0 ||
					this.level().getBestNeighborSignal(this.blockPosition().below()) > 0;
			if (hasPower != this.powered) {
				if (hasPower) {
					this.fireServer();
				}
				this.powered = hasPower;
			}

			this.movementTick();
			this.move(MoverType.SELF, this.getDeltaMovement());
		}
	}

	@Override
	public void onPassengerTurned(@NotNull Entity passenger) {
		if (this.level().isClientSide() && passenger instanceof Player player && player.isLocalPlayer()) {
			if (chained) {
				player.setYRot(this.getYRot());
				player.setXRot(this.getXRot());
			} else {
				player.setXRot(Math.min(18, player.getXRot()));
			}
		}
	}

	public boolean canPlayerModify(Player player) {
		return this.alwaysModifiable || player.mayBuild();
	}

	@Override
	public @NotNull InteractionResult interact(@NotNull Player player, @NotNull InteractionHand hand) {
		ItemStack handItem = player.getItemInHand(hand);
		// 如果手持火药，填充到大炮中
		if(!player.isShiftKeyDown()) {
			if (testAndInteract(player, handItem)) return InteractionResult.sidedSuccess(level().isClientSide());
		}
		if (player == this.getFirstPassenger()) {
			return super.interact(player, hand);
		}

		if (player.isShiftKeyDown()) {
			if (!level().isClientSide()) {
				if (this.canPlayerModify(player)) {
					player.openMenu(new SimpleMenuProvider((syncId, playerInv, user) ->
							new CannonContainerMenu(syncId, playerInv, this.inventory), UI_TITLE));
				} else {
					level().playSound(null, this.blockPosition(), SoundEvents.CHEST_LOCKED, SoundSource.BLOCKS, 0.5f, 1.5f);
				}
				return InteractionResult.PASS;
			}
			return InteractionResult.SUCCESS;
		}

		if (!this.isVehicle() && !this.getBehavior().occupiesCannon(this.inventory.getItem(2))) {
			if (!level().isClientSide()) {
				player.setYRot(this.getYRot());
				player.setXRot(this.getXRot());
				player.startRiding(this);

				return InteractionResult.PASS;
			}
			return InteractionResult.SUCCESS;
		} else {
			player.displayClientMessage(FULL_CANNON_DIALOG, true);
		}

		return super.interact(player, hand);
	}

	private boolean testAndInteract(Player player, @NotNull ItemStack stack) {
		if (stack.is(Items.GUNPOWDER)) {
			handlerGunPower(player, stack);
			return true;
		} else if (stack.is(Items.CHAIN)) {
			handlerChain(player, stack);
			return true;
		} else if (CannonBehavior.isValidBehaviorStack(stack) && this.getPassengers().isEmpty()) {
			handlerBehaviorItem(player, stack);
			return true;
		} else if (stack.is(Items.FLINT_AND_STEEL)) {
			player.displayClientMessage(FIRE, true);
			fireServer();
			return true;
		}
		return false;
	}

	private void handlerGunPower(Player player, ItemStack stack) {
		if (!level().isClientSide()) {
			ItemStack currentGunpowder = inventory.getItem(0);
			// 如果炮内已有火药，先还给玩家
			if (currentGunpowder.getCount() >= currentGunpowder.getMaxStackSize()) {
				if (!player.isCreative() && !player.getInventory().add(currentGunpowder)) {
					// 如果背包满了，掉落在地上
					player.drop(currentGunpowder, false);
				}
			}
			// 设置新的火药到炮中
			ItemStack newGunpowder = stack.copy();
			newGunpowder.setCount(currentGunpowder.getCount() + 1); // 只放一个火药
			((ServerPlayer) player).sendSystemMessage(Component.translatable(ADD_GUNPOWDER_DIALOG, Math.min(currentGunpowder.getCount() + 1, currentGunpowder.getMaxStackSize()), currentGunpowder.getMaxStackSize()), true);
			inventory.setItem(0, newGunpowder);

			if (!player.isCreative()) {
				stack.shrink(1);
			}

			level().playSound(null, this.blockPosition(), SoundEvents.SAND_PLACE, SoundSource.BLOCKS, 1.0f, 1.0f);
		}
	}

	private void handlerChain(Player player, ItemStack stack) {
		if (!level().isClientSide()) {
			ItemStack currentChain = inventory.getItem(1);
			if (currentChain.isEmpty()) { // 添加锁链Pl
				ItemStack item = stack.copy();
				item.setCount(1);
				inventory.setItem(1, item);
				if (!player.isCreative()) {
					stack.shrink(1);
				}
				((ServerPlayer) player).sendSystemMessage(Component.translatable(ADD_LOCK_CHAIN_DIALOG).withStyle(ChatFormatting.GREEN), true);
				level().playSound(null, this.blockPosition(), SoundEvents.CHAIN_PLACE, SoundSource.BLOCKS, 1.0f, 1.0f);
			} else {
				inventory.setItem(1, ItemStack.EMPTY);
				if (!player.isCreative()) { // 移除锁链
					if (!player.getInventory().add(currentChain)) {
						player.drop(currentChain, false);
					}
				}
				((ServerPlayer) player).sendSystemMessage(Component.translatable(REMOVE_LOCK_CHAIN_DIALOG).withStyle(ChatFormatting.RED), true);
				level().playSound(null, this.blockPosition(), SoundEvents.CHAIN_FALL, SoundSource.BLOCKS, 1.0f, 1.0f);
			}
		}
	}

	private void handlerBehaviorItem(Player player, ItemStack stack) {
		if (!level().isClientSide()) {
			ItemStack currentBehaviorItem = inventory.getItem(2);
			if (currentBehaviorItem.isEmpty()) { // 添加物品
				ItemStack copied = stack.copy();
				copied.setCount(1);
				inventory.setItem(2, copied);
				if (!player.isCreative()) {
					stack.shrink(1);
				}
				level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f);
			} else {
				if (stack.is(currentBehaviorItem.getItem())) { //同类物品 +1
					if (stack.getCount() >= currentBehaviorItem.getMaxStackSize()) {
						if (!player.isCreative() && !player.getInventory().add(currentBehaviorItem)) {
							player.drop(currentBehaviorItem, false);
						}
					}
					ItemStack stack1 = stack.copy();
					stack1.setCount(currentBehaviorItem.getCount() + 1);
					((ServerPlayer) player).sendSystemMessage(Component.translatable(ADD_BEHAVIOR_DIALOG, I18n.get(currentBehaviorItem.getDescriptionId()),Math.min(currentBehaviorItem.getCount() + 1, currentBehaviorItem.getMaxStackSize()), currentBehaviorItem.getMaxStackSize()), true);
					inventory.setItem(2, stack1);

					if (!player.isCreative()) {
						stack.shrink(1);
					}

					level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f);
				} else { //不同类替换
					if (!player.isCreative() && !player.getInventory().add(currentBehaviorItem)) {
						player.drop(currentBehaviorItem, false);
					}
					ItemStack copied = stack.copy();
					copied.setCount(1);
					inventory.setItem(2, copied);
					((ServerPlayer) player).sendSystemMessage(Component.translatable(ADD_BEHAVIOR_DIALOG, I18n.get(stack.getDescriptionId()), 1, stack.getMaxStackSize()), true);
					if (!player.isCreative()) {
						stack.shrink(1);
					}
					level().playSound(null, this.blockPosition(), getBehavior().getPlaceInSound(), SoundSource.BLOCKS, 1.0f, 1.0f);
				}
			}
		}
	}

	@Override
	public boolean skipAttackInteraction(@NotNull Entity attacker) {
		if (attacker instanceof Player player && player != this.getFirstPassenger()) {
			ItemStack is = player.getItemInHand(InteractionHand.MAIN_HAND);
			if (player.mayBuild() &&
					(player.isCreative() || is.getItem() instanceof PickaxeItem)) {
				if (!this.level().isClientSide()) {
					Containers.dropContents(this.level(), this.blockPosition(), this.inventory);
					if (!player.isCreative()) {
						Containers.dropItemStack(this.level(), this.getX(), this.getY(), this.getZ(), new ItemStack(BTRItems.CANNON_ITEM.get()));
					}
					this.remove(RemovalReason.KILLED);
				}
				this.level().playSound(null, this.blockPosition(), SoundEvents.STONE_BREAK, SoundSource.BLOCKS,
						1, 0.8f);
				this.level().addDestroyBlockEffect(this.blockPosition(), Blocks.ANVIL.defaultBlockState());

				return true;
			}
		}

		this.level().playSound(null, this.blockPosition(), SoundEvents.STONE_HIT, SoundSource.BLOCKS,
				1, 0.5f);
		return true;
	}

	@Override
	public void lerpTo(double x, double y, double z, float yaw, float pitch, int interpolationSteps, boolean interpolate) {
		if (!this.level().isClientSide()) {
			this.setPos(x, y, z);

			if (!this.isVehicle()) {
				this.setRot(yaw, pitch);
			}
		} else {
			this.targetX = x;
			this.targetY = y;
			this.targetZ = z;
			this.targetTicks = this.getType().updateInterval();
		}
	}

	private void positionTrackTick() {
		if (this.targetTicks > 0) {
			this.setPos(
					this.getX() + (this.targetX - this.getX()) / (double)this.targetTicks,
					this.getY() + (this.targetY - this.getY()) / (double)this.targetTicks,
					this.getZ() + (this.targetZ - this.getZ()) / (double)this.targetTicks
			);

			this.targetTicks--;
		}
	}

	private void movementTick() {
		var vel = this.getDeltaMovement();

		this.setDeltaMovement(new Vec3(vel.x * 0.9, this.onGround() ? 0 : Math.max(vel.y - 0.07, -0.7), vel.z * 0.9));
		this.hurtMarked = true;
	}

	public ItemStack getBehaviorStack() {
		if (!this.level().isClientSide()) {
			return this.inventory.getItem(2);
		}

		return this.entityData.get(BEHAVIOR_STACK);
	}

	public void handleInput(boolean firing) {
		if (this.level().isClientSide()) {
			if (firing && !this.firing) {
				BTRNetwork.CHANNEL.sendToServer(new RequestFirePayload(this));
			}
			this.firing = firing;
		}
	}

	public void fireServer() {
		if (this.level() instanceof ServerLevel world) {
			var gunpowder = this.inventory.getItem(0);
			if (gunpowder.is(Items.GUNPOWDER) && gunpowder.getCount() > 0) {
				CannonFireEvent.Pre pre;
				Player firedPlayer = null;
				var behaviorStack = this.getBehaviorStack();
				if(getFirstPassenger() instanceof Player player) {
					firedPlayer = player;
				}
				pre = new CannonFireEvent.Pre(this, firedPlayer, behaviorStack);
				if(pre.isCanceled()) return;
				var vel = getDeltaMovement().add(getLookAngle().scale(Math.sqrt(gunpowder.getCount()) * 0.6));

				CannonBehavior behavior = this.getBehavior();
				if (firedPlayer != null) {

					// 关键修改：先设置速度，再停止乘坐
					firedPlayer.setDeltaMovement(vel);
					firedPlayer.hurtMarked = true; // 强制同步运动状态

					// 然后停止乘坐
					firedPlayer.stopRiding();

					// 设置飞行状态
					((PlayerEntityDuck)firedPlayer).blasttravel$setCannonFlight(true);

                }
				if(pre.isCanceled()) return;
				behavior.onFired(this, behaviorStack, vel);
				this.level().playSound(null, this.blockPosition(), SoundEvents.GENERIC_EXPLODE, SoundSource.BLOCKS, 1, 1);
				for (var to : world.players()) {
					BTRNetwork.CHANNEL.sendTo(new FireCannonPayload(this, firedPlayer, vel), to.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
				}

				this.updateStateFromInventory();
			} else {
				if (this.getFirstPassenger() instanceof Player player) {
					player.stopRiding();
					player.displayClientMessage(NO_GUNPOWDER_DIALOG, true);
				}

				this.level().playSound(null, this.blockPosition(), SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1, 0.8f);
			}
		}
	}

	public void fireClient() {
		if (!level().isClientSide()) {
			return;
		}

		this.animate();

		final int ringParticles = 18;
		for (int i = 0; i < ringParticles; i++) {
			double angle = (2d / ringParticles) * Math.PI * i;
			var arc = new Vec3(Math.sin(angle), Math.cos(angle), 0)
					.xRot(-this.getXRot() * Mth.DEG_TO_RAD)
					.yRot(-this.getYRot() * Mth.DEG_TO_RAD);

			var pos = this.position()
					.add(0, 0.75, 0)
					.add(this.calculateViewVector(this.getXRot() - 4, this.getYRot())
							.scale(1.69f)).add(arc.scale(0.15f));
			var vel = arc.scale(0.14);

			Minecraft.getInstance().particleEngine.createParticle(BTRParticleTypes.CANNON_BLAST.get(),
					pos.x, pos.y, pos.z, vel.x, vel.y, vel.z);
		}
	}

	public void animate() {
		this.animation = MAX_ANIMATION;
	}

	protected int getBehaviorId() {
		return this.entityData.get(BEHAVIOR);
	}

	protected void setBehaviorId(int id) {
		this.entityData.set(BEHAVIOR, id);
	}

	public boolean hasFuse() {
		return this.isVehicle() || this.getBehavior().occupiesCannon(this.inventory.getItem(2));
	}

	public boolean hasChains() {
		return this.chained;
	}

	public float getAnimation(float tickDelta) {
		float anim = Math.max(0, this.animation - tickDelta);
		return anim / MAX_ANIMATION;
	}

	public int getAnimationTick() {
		return this.animation;
	}

	private void setChained(boolean chained) {
		if (chained != this.chained) {
			this.level().playSound(null, this.blockPosition(), SoundEvents.ARMOR_EQUIP_CHAIN,
					SoundSource.BLOCKS, 1, 1.2f);
		}

		this.chained = chained;
	}

	@OnlyIn(Dist.CLIENT)
	public @Nullable AbstractClientPlayer getClientPlayer() {
		return this.getFirstPassenger() instanceof AbstractClientPlayer player ? player : null;
	}

	public CannonBehavior getBehavior() {
		return CannonBehavior.byId(getBehaviorId());
	}

	protected void updateStateFromInventory() {
		if (!this.level().isClientSide()) {
			for (int slot = 0; slot < this.inventory.getContainerSize(); slot++) {
				var stack = this.inventory.getItem(slot);
				if (slot == 1) {
					setChained(stack.is(Items.CHAIN));
				} else if (slot == 2) {
					this.setBehaviorId(CannonBehavior.idForStack(stack));
					this.entityData.set(BEHAVIOR_STACK, stack);
				}
			}
		}
	}

	@Nullable
	@Override
	public ItemStack getPickResult() {
		return new ItemStack(BTRItems.CANNON_ITEM.get());
	}

	@Override
	public boolean isPickable() {
		return !this.isRemoved();
	}

	@Override
	public boolean canCollideWith(Entity other) {
		return (other.canBeCollidedWith() || other.isPushable()) && !this.isPassengerOfSameVehicle(other);
	}

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

	@Override
	public double getPassengersRidingOffset() {
		if (this.isVehicle()) {
			var passenger = this.getFirstPassenger();
            if (passenger != null) {
                return (-passenger.getMyRidingOffset() - passenger.getEyeHeight(passenger.getPose())) + 0.75f;
            }
        }
		return super.getPassengersRidingOffset();
	}


	@Override
	protected void defineSynchedData() {
		this.entityData.define(BEHAVIOR, 0);
		this.entityData.define(CHAINED, false);
		this.entityData.define(BEHAVIOR_STACK, ItemStack.EMPTY);
	}

	@Override
	protected void readAdditionalSaveData(CompoundTag nbt) {
		ContainerHelper.loadAllItems(nbt.getCompound("Items"), this.inventory.items);
		this.powered = nbt.getBoolean("powered");
		this.alwaysModifiable = nbt.getBoolean("alwaysModifiable");

		this.updateStateFromInventory();
	}

	@Override
	protected void addAdditionalSaveData(CompoundTag nbt) {
		var inv = new CompoundTag();
		ContainerHelper.saveAllItems(inv, this.inventory.items);
		nbt.put("Items", inv);
		nbt.putBoolean("powered", this.powered);
		nbt.putBoolean("alwaysModifiable", this.alwaysModifiable);
	}

}
