use crate::{
    GameState, billboards::Billboard, global_observer, loading_assets::GameAssets,
    physics_layers::GameLayer, player::Player, sounds::PlaySound,
    squish_animation::SquishAnimation,
};
use avian3d::prelude::*;
use bevy::prelude::*;
use std::f32::consts::PI;

#[derive(Event, Reflect)]
pub struct KeySpawn(pub Vec3, pub String);

#[derive(Component, Reflect)]
#[reflect(Component)]
struct Key(pub String);

#[derive(Event, Reflect)]
pub struct KeyCollected(pub String);

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

    global_observer!(app, on_spawn_key);
}

fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<GameAssets>) {
    let KeySpawn(position, id) = trigger.event();

    let angle = rand::random::<f32>() * PI * 2.;
    let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize();

    commands.spawn((
        Name::new("key"),
        Key(id.clone()),
        Transform::from_translation(*position),
        Visibility::default(),
        Collider::sphere(1.5),
        ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
        LockedAxes::ROTATION_LOCKED,
        RigidBody::Dynamic,
        CollisionLayers::new(LayerMask(GameLayer::Collectibles.to_bits()), LayerMask::ALL),
        CollisionEventsEnabled,
        Restitution::new(0.6),
        Children::spawn(Spawn((
            Billboard,
            SquishAnimation(2.6),
            SceneRoot(assets.mesh_key.clone()),
        ))),
    ));
}

fn collect_key(
    mut commands: Commands,
    mut collision_event_reader: EventReader<CollisionStarted>,
    query_player: Query<&Player>,
    query_collectable: Query<&Key>,
) {
    for CollisionStarted(e1, e2) in collision_event_reader.read() {
        let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) {
            *e2
        } else if query_player.contains(*e2) && query_collectable.contains(*e1) {
            *e1
        } else {
            continue;
        };

        let key = query_collectable.get(collectable).unwrap();

        commands.trigger(PlaySound::KeyCollect);
        commands.trigger(KeyCollected(key.0.clone()));
        commands.entity(collectable).despawn();
    }
}
