package net.mcreator.phayriosisreborn.meteor;

import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.ItemSupplier;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.network.PlayMessages;
import net.minecraftforge.registries.ForgeRegistries;

/**
 * Live meteor projectile retained from Phase 5. Phase 6 now hands impact to the real explosion/terrain system.
 * The entity steers toward one server-selected impact point at constant speed.
 */
public final class PhayriosisMeteorEntity extends AbstractArrow implements ItemSupplier {
    private static final double IMPACT_DISTANCE_SQR = 9.0D;
    private static final double MAX_LIFETIME_TICKS = 1200.0D;

    private double targetX;
    private double targetY;
    private double targetZ;
    private double meteorSpeed = 1.8D;
    private double meteorLife = 0.0D;
    private boolean pathInitialized = false;
    private boolean impactHandled = false;
    // 0.0.16 persisted coordinates used to reconstruct temporary ticking chunk tickets.
    private double leaseStartX;
    private double leaseStartZ;

    public PhayriosisMeteorEntity(EntityType<? extends PhayriosisMeteorEntity> type, Level level) {
        super(type, level);
        // EntityCulling 1.9.4 checks this vanilla ignore-culling flag.
        this.f_19811_ = true;
        this.m_20242_(true);
    }

    public PhayriosisMeteorEntity(PlayMessages.SpawnEntity packet, Level level) {
        this(PhayriosisMeteorEntityRegistry.METEOR, level);
    }

    @Override
    public Packet<ClientGamePacketListener> m_5654_() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }

    @Override
    public ItemStack m_7846_() {
        Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation("phayriosisreborn", "meteoric_rock"));
        return new ItemStack((ItemLike) block);
    }

    @Override
    protected ItemStack m_7941_() {
        return m_7846_();
    }

    public void initializePath(double x, double y, double z, double speed) {
        this.leaseStartX = this.m_20185_();
        this.leaseStartZ = this.m_20189_();
        this.targetX = x;
        this.targetY = y;
        this.targetZ = z;
        this.meteorSpeed = sanitizeSpeed(speed);
        this.pathInitialized = true;
        this.m_20242_(true);
        steerTowardTarget();
    }

    @Override
    public void m_8119_() {
        this.m_20242_(true);
        if (pathInitialized && !impactHandled) steerTowardTarget();
        super.m_8119_();
        meteorLife += 1.0D;

        if (!this.m_9236_().m_5776_() && pathInitialized && !impactHandled) {
            if (this.m_9236_() instanceof ServerLevel serverLevel) {
                // A broad wake every other tick keeps the spectacle strong without
                // turning one meteor into unbounded packet/particle spam.
                if (((long) meteorLife & 1L) == 0L) emitAtmosphericEntryEffects(serverLevel);
                emitFlightSounds(serverLevel);
            }

            double dx = targetX - this.m_20185_();
            double dy = targetY - this.m_20186_();
            double dz = targetZ - this.m_20189_();
            if (dx * dx + dy * dy + dz * dz <= IMPACT_DISTANCE_SQR) {
                finishImpact();
            } else if (meteorLife >= MAX_LIFETIME_TICKS) {
                finishImpact();
            }
        }
    }

    @Override
    public void m_8060_(BlockHitResult result) {
        if (!this.m_9236_().m_5776_()) finishImpact();
    }

    @Override
    public void m_5790_(EntityHitResult result) {
        // Deliberately pass through entities in Phase 5. Terrain impact owns the event.
    }

    /**
     * Wide atmospheric-entry envelope sized for the deliberately huge 60x render.
     * The old 1-block-ish spreads formed a thin line through the center. This version
     * makes a turbulent rear-face wake with multiple depths and a wide incandescent bow.
     */
    private void emitAtmosphericEntryEffects(ServerLevel level) {
        double dx = targetX - this.m_20185_();
        double dy = targetY - this.m_20186_();
        double dz = targetZ - this.m_20189_();
        double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
        if (len < 1.0E-6D) return;

        double ux = dx / len;
        double uy = dy / len;
        double uz = dz / len;

        // Build a perpendicular basis so side plumes follow the actual flight direction.
        double horizontal = Math.sqrt(ux * ux + uz * uz);
        double rx, ry, rz;
        if (horizontal > 1.0E-5D) {
            rx = -uz / horizontal;
            ry = 0.0D;
            rz = ux / horizontal;
        } else {
            rx = 1.0D;
            ry = 0.0D;
            rz = 0.0D;
        }
        double vx = ry * uz - rz * uy;
        double vy = rz * ux - rx * uz;
        double vz = rx * uy - ry * ux;

        double x = this.m_20185_();
        double y = this.m_20186_();
        double z = this.m_20189_();

        // Broad, hot bow on the leading face.
        double frontX = x + ux * 4.8D;
        double frontY = y + uy * 4.8D;
        double frontZ = z + uz * 4.8D;
        sendVisibleParticles(level, ParticleTypes.f_123744_, frontX, frontY, frontZ, 36, 9.0D, 9.0D, 9.0D, 0.045D);
        sendVisibleParticles(level, ParticleTypes.f_123756_, frontX, frontY, frontZ, 5, 7.0D, 7.0D, 7.0D, 0.025D);

        // Main turbulent wake behind the full rear face rather than one center point.
        double rearX = x - ux * 7.5D;
        double rearY = y - uy * 7.5D;
        double rearZ = z - uz * 7.5D;
        sendVisibleParticles(level, ParticleTypes.f_123744_, rearX, rearY, rearZ, 28, 10.5D, 10.5D, 10.5D, 0.055D);
        sendVisibleParticles(level, ParticleTypes.f_123755_, rearX, rearY, rearZ, 24, 12.0D, 12.0D, 12.0D, 0.035D);

        // Two asymmetric side plumes make the trail billow instead of drawing a straight line.
        double side = 9.0D;
        double sideAX = rearX + rx * side + vx * 2.0D;
        double sideAY = rearY + ry * side + vy * 2.0D;
        double sideAZ = rearZ + rz * side + vz * 2.0D;
        double sideBX = rearX - rx * side - vx * 1.5D;
        double sideBY = rearY - ry * side - vy * 1.5D;
        double sideBZ = rearZ - rz * side - vz * 1.5D;
        sendVisibleParticles(level, ParticleTypes.f_123744_, sideAX, sideAY, sideAZ, 16, 6.0D, 6.0D, 6.0D, 0.045D);
        sendVisibleParticles(level, ParticleTypes.f_123755_, sideAX, sideAY, sideAZ, 14, 7.5D, 7.5D, 7.5D, 0.03D);
        sendVisibleParticles(level, ParticleTypes.f_123744_, sideBX, sideBY, sideBZ, 16, 6.0D, 6.0D, 6.0D, 0.045D);
        sendVisibleParticles(level, ParticleTypes.f_123755_, sideBX, sideBY, sideBZ, 14, 7.5D, 7.5D, 7.5D, 0.03D);

        // Thick smoke column expands as it falls away from the meteor.
        double midX = x - ux * 17.0D;
        double midY = y - uy * 17.0D;
        double midZ = z - uz * 17.0D;
        sendVisibleParticles(level, ParticleTypes.f_123755_, midX, midY, midZ, 24, 12.0D, 12.0D, 12.0D, 0.025D);
        sendVisibleParticles(level, ParticleTypes.f_123762_, midX, midY, midZ, 24, 9.0D, 9.0D, 9.0D, 0.03D);

        double tailX = x - ux * 31.0D;
        double tailY = y - uy * 31.0D;
        double tailZ = z - uz * 31.0D;
        sendVisibleParticles(level, ParticleTypes.f_123762_, tailX, tailY, tailZ, 28, 15.0D, 15.0D, 15.0D, 0.025D);
    }

    /**
     * Positional server sounds intentionally use normal SoundEvent playback. This keeps
     * Sound Physics Remastered and other sound-engine mods in the normal processing path.
     */
    private void emitFlightSounds(ServerLevel level) {
        long life = (long) meteorLife;
        if (life % 8L == 1L) {
            // User-supplied meteor-flyby audio was converted into four short mono turbulent
            // grains and registered under this one SoundEvent. Each emission randomly chooses
            // a grain. Mono assets preserve true positional attenuation/spatialization, and
            // the 8-tick cadence gives overlapping movement without a long post-impact tail.
            playServerSound(level, "phayriosisreborn:meteor_flight", SoundSource.AMBIENT, 1.35F, 1.0F);
        }
        if (life % 8L == 1L) {
            playServerSound(level, "minecraft:block.fire.ambient", SoundSource.AMBIENT, 0.0F, 0.72F); // disabled; visuals carry the fire layer
        }
    }

    private void playServerSound(ServerLevel level, String id, SoundSource source, float volume, float pitch) {
        SoundEvent sound = ForgeRegistries.SOUND_EVENTS.getValue(new ResourceLocation(id));
        if (sound == null) return;
        level.m_5594_(null, BlockPos.m_274561_(this.m_20185_(), this.m_20186_(), this.m_20189_()), sound, source, volume, pitch);
    }

    private static void sendVisibleParticles(ServerLevel level, ParticleOptions particle,
                                             double x, double y, double z, int count,
                                             double spreadX, double spreadY, double spreadZ, double speed) {
        for (ServerPlayer player : level.m_6907_()) {
            double dx = player.m_20185_() - x;
            double dy = player.m_20186_() - y;
            double dz = player.m_20189_() - z;
            if (dx * dx + dy * dy + dz * dz <= 262144.0D) {
                level.m_8624_(player, particle, true, x, y, z, count, spreadX, spreadY, spreadZ, speed);
            }
        }
    }

    private void steerTowardTarget() {
        double dx = targetX - this.m_20185_();
        double dy = targetY - this.m_20186_();
        double dz = targetZ - this.m_20189_();
        double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
        if (len < 1.0E-6D) return;
        double scale = meteorSpeed / len;
        this.m_20256_(new Vec3(dx * scale, dy * scale, dz * scale));
    }

    private void finishImpact() {
        if (impactHandled) return;
        impactHandled = true;
        if (this.m_9236_() instanceof ServerLevel serverLevel) {
            // Phase 6: the impact system owns the genuine explosion and Phayriosis
            // terrain deposition. The old manual explosion sound is no longer authoritative.
            PhayriosisMeteorChunkLoader.releaseFlightLater(serverLevel, this, leaseStartX, leaseStartZ, targetX, targetZ);
            PhayriosisMeteorImpactSystem.createImpact(serverLevel, targetX, targetY, targetZ, this);
        }
        this.m_146870_();
    }

    @Override
    public void m_7380_(CompoundTag tag) {
        super.m_7380_(tag);
        tag.m_128347_("MeteorTargetX", targetX);
        tag.m_128347_("MeteorTargetY", targetY);
        tag.m_128347_("MeteorTargetZ", targetZ);
        tag.m_128347_("MeteorSpeed", meteorSpeed);
        tag.m_128347_("MeteorLife", meteorLife);
        tag.m_128379_("MeteorPathInitialized", pathInitialized);
        tag.m_128347_("MeteorLeaseStartX", leaseStartX);
        tag.m_128347_("MeteorLeaseStartZ", leaseStartZ);
    }

    @Override
    public void m_7378_(CompoundTag tag) {
        super.m_7378_(tag);
        if (tag.m_128441_("MeteorTargetX")) targetX = tag.m_128459_("MeteorTargetX");
        if (tag.m_128441_("MeteorTargetY")) targetY = tag.m_128459_("MeteorTargetY");
        if (tag.m_128441_("MeteorTargetZ")) targetZ = tag.m_128459_("MeteorTargetZ");
        if (tag.m_128441_("MeteorSpeed")) meteorSpeed = sanitizeSpeed(tag.m_128459_("MeteorSpeed"));
        if (tag.m_128441_("MeteorLife")) meteorLife = tag.m_128459_("MeteorLife");
        if (tag.m_128441_("MeteorPathInitialized")) pathInitialized = tag.m_128471_("MeteorPathInitialized");
        this.m_20242_(true);
    }

    private static double sanitizeSpeed(double value) {
        if (Double.isNaN(value) || Double.isInfinite(value) || value <= 0.05D) return 1.8D;
        return Math.min(value, 6.0D);
    }
}
