use std::f32::consts::TAU;

use bevy::prelude::*;
use rand::Rng;

use crate::{
    core::{
        game_state::DispawnOnGameOver,
        kirby::Kirby,
        life::{DamageDealer, Life},
        wave::BubbleSplash,
    },
    physics::{density_grid::DensityObject, physics_body::PhysicsBody, sphere_collider::SphereCollider},
};

#[derive(Component)]
#[require(Sprite, PhysicsBody, DensityObject, Life, DamageDealer, SphereCollider, DispawnOnGameOver)]
pub struct Bubble {
    move_force: f32,
}

// pub fn bubble_spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
//     bubble_spawn_wave(&mut commands, &asset_server, 1000);
// }

pub fn bubble_spawn_wave(commands: &mut Commands, asset_server: &Res<AssetServer>, bubble_splashes: &[BubbleSplash]) {
    let texture: Handle<Image> = asset_server.load("sprites/bubble.png");
    let mut rng = rand::rng();

    for BubbleSplash { bubble_type, center, radius, nb_bubbles } in bubble_splashes {
        for _ in 0..*nb_bubbles {
            let angle = rng.random_range(0.0..TAU);
            let spawn_radius = rng.random_range(0.0..1.0f32).sqrt() * radius;
            let pos = center + Vec2::from_angle(angle) * spawn_radius;

            commands.spawn((
                Bubble { move_force: bubble_type.move_force },
                Sprite { image: texture.clone(), color: bubble_type.color, ..default() },
                PhysicsBody { mass: 10.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.05 },
                Transform::from_translation(pos.extend(0.0)).with_scale(Vec3::splat(1.0)),
                Life::new(bubble_type.max_life),
                DamageDealer::new(0.5),
                SphereCollider::new(20.0),
            ));
        }
    }
}

pub fn bubble_move(
    mut bubble_query: Query<(&mut PhysicsBody, &Transform, &Bubble), With<Bubble>>,
    kirby_query: Query<(&Transform, &Kirby), With<Kirby>>,
) {
    for (mut bubble_body, bubble_transform, bubble) in &mut bubble_query {
        let mut nearest_kirby_pos = Vec3::ZERO;
        let mut nearest_squared = f32::MAX;

        for (kirby_transform, _kirby) in kirby_query {
            let dist_squared = kirby_transform.translation.distance_squared(bubble_transform.translation);
            if (dist_squared) < nearest_squared {
                nearest_squared = dist_squared;
                nearest_kirby_pos = kirby_transform.translation;
            }
        }

        let dir = (nearest_kirby_pos - bubble_transform.translation).normalize().xy();
        bubble_body.force += dir * bubble.move_force;
    }
}

pub struct BubblePlugin;

impl Plugin for BubblePlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(FixedUpdate, bubble_move);
    }
}
