use crate::{
    GameState, abilities::Healing, loading_assets::GameAssets, utils::billboards::Billboard,
};
use bevy::prelude::*;
use rand::{Rng, thread_rng};

#[derive(Component, Default)]
#[require(Transform, InheritedVisibility)]
struct HealParticleEffect {
    next_spawn: f32,
}

#[derive(Component)]
struct HealParticle {
    start_scale: f32,
    end_scale: f32,
    start_pos: Vec3,
    end_pos: Vec3,
    start_time: f32,
    life_time: f32,
}

pub fn plugin(app: &mut App) {
    app.add_systems(
        Update,
        (on_added, update_effects, update_particles).run_if(in_state(GameState::Playing)),
    );
}

fn on_added(mut cmds: Commands, query: Query<&Healing, Added<Healing>>) {
    for healing in query.iter() {
        cmds.entity(healing.0).insert((
            Name::new("heal-particle-effect"),
            HealParticleEffect::default(),
        ));
    }
}

fn update_effects(
    mut cmds: Commands,
    mut query: Query<(&mut HealParticleEffect, Entity)>,
    time: Res<Time>,
    assets: Res<GameAssets>,
) {
    const DISTANCE: f32 = 4.;
    let mut rng = thread_rng();

    let now = time.elapsed_secs();
    for (mut effect, e) in query.iter_mut() {
        if effect.next_spawn < now {
            let start_pos = Vec3::new(
                rng.gen_range(-DISTANCE..DISTANCE),
                2.,
                rng.gen_range(-DISTANCE..DISTANCE),
            );
            let max_distance = start_pos.length().max(0.8);
            let end_pos =
                start_pos + (start_pos.normalize() * -1.) * rng.gen_range(0.5..max_distance);
            let start_scale = rng.gen_range(0.7..1.0);
            let end_scale = rng.gen_range(0.1..start_scale);

            cmds.entity(e).with_child((
                Name::new("heal-particle"),
                SceneRoot(assets.mesh_heal_particle.clone()),
                Billboard,
                Transform::from_translation(start_pos),
                HealParticle {
                    start_scale,
                    end_scale,
                    start_pos,
                    end_pos,
                    start_time: now,
                    life_time: rng.gen_range(0.3..1.0),
                },
            ));

            effect.next_spawn = now + rng.gen_range(0.1..0.5);
        }
    }
}

fn update_particles(
    mut cmds: Commands,
    mut query: Query<(&mut Transform, &HealParticle, Entity)>,
    time: Res<Time>,
) {
    for (mut transform, particle, e) in query.iter_mut() {
        if particle.start_time + particle.life_time < time.elapsed_secs() {
            cmds.entity(e).despawn();
            continue;
        }

        let t = (time.elapsed_secs() - particle.start_time) / particle.life_time;

        // info!("particle[{e:?}] t: {t}");
        transform.translation = particle.start_pos.lerp(particle.end_pos, t);
        transform.scale = Vec3::splat(particle.start_scale.lerp(particle.end_scale, t));
    }
}
