use crate::{
    core::game_state::{DispawnOnGameOver, GameStartupSet},
    juice::sound::Suction,
};
use bevy::prelude::*;
use bevy_kira_audio::prelude::*;

const KIRBY_SCALE: f32 = 3.0;

use crate::{
    core::{
        game_state::GameState,
        life::{DamageDealer, Life},
    },
    juice::animation::{AnimationIndices, AnimationTimer},
    physics::{physics_body::PhysicsBody, sphere_collider::SphereCollider},
};

#[derive(Component)]
#[require(Transform, SphereCollider, DamageDealer, DispawnOnGameOver)]
pub struct SuckArea;

#[derive(Component)]
#[require(Sprite, PhysicsBody, SphereCollider, AnimationIndices, Life, DispawnOnGameOver)]
pub struct Kirby {
    pub speed_force: f32,
    pub is_sucking: bool,
    pub shield_factor: f32, // 1.0 receive full damage, 0.1 receive 10% of damage
}

pub fn kirby_spawn_start(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) {
    println!("Start Kirby");
    kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3::ZERO);
    // kirby_spawn(&mut commands, &asset_server, &mut texture_atlas_layouts, Vec3 { x: 500.0, y: 0.0, z: 0.0 });
}

pub fn kirby_spawn(
    commands: &mut Commands,
    asset_server: &Res<AssetServer>,
    texture_atlas_layouts: &mut ResMut<Assets<TextureAtlasLayout>>,
    pos: Vec3,
) {
    let texture: Handle<Image> = asset_server.load("sprites/Sprite_kirby-Sheet.png");
    let layout = TextureAtlasLayout::from_grid(UVec2::splat(32), 3, 3, None, None);
    let texture_atlas_layout = texture_atlas_layouts.add(layout);
    let animation_indices = AnimationIndices { first: 3, last: 5 }; // idle

    let kirby_entity = commands
        .spawn((
            Kirby { speed_force: 10000.0, is_sucking: false, shield_factor: 1.0 },
            Sprite::from_atlas_image(
                texture,
                TextureAtlas { layout: texture_atlas_layout, index: animation_indices.first },
            ),
            PhysicsBody { mass: 10.0, force: Vec2::ZERO, velocity: Vec2::ZERO, drag: 0.05 },
            SphereCollider::new(20.0),
            animation_indices,
            AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
            Life::new(10.0),
            Transform::from_translation(pos).with_scale(Vec3::splat(KIRBY_SCALE)),
        ))
        .id();

    commands.entity(kirby_entity).with_child((
        Transform::from_xyz(40.0, 0.0, 0.0),
        SuckArea,
        SphereCollider::new(80.0),
        DamageDealer::new(100.0),
    ));
}

pub fn get_dir(keys: Res<ButtonInput<KeyCode>>) -> Vec2 {
    let mut dir = Vec2::ZERO;

    if keys.pressed(KeyCode::KeyW) {
        dir.y += 1.0;
    }
    if keys.pressed(KeyCode::KeyS) {
        dir.y -= 1.0;
    }
    if keys.pressed(KeyCode::KeyA) {
        dir.x -= 1.0;
    }
    if keys.pressed(KeyCode::KeyD) {
        dir.x += 1.0;
    }

    dir.normalize_or_zero()
}

pub fn kirby_actions(
    keys: Res<ButtonInput<KeyCode>>,
    mut query: Query<(&mut PhysicsBody, &mut Kirby, &mut AnimationIndices, &mut Transform), With<Kirby>>,
    asset_server: Res<AssetServer>,
    suction_audio: Res<AudioChannel<Suction>>,
) {
    let space_just_pressed = keys.just_pressed(KeyCode::Space);
    let space_just_released = keys.just_released(KeyCode::Space);
    let dir = get_dir(keys);
    for (mut body, mut kirby, mut anim_indices, mut transform) in &mut query {
        if space_just_released {
            kirby.is_sucking = false;
            suction_audio.stop();
        };

        if space_just_pressed {
            kirby.is_sucking = true;
            anim_indices.change(0, 2);
            suction_audio.play(asset_server.load("sounds/suction.ogg"));
        };

        if kirby.is_sucking {
            continue;
        };
        if dir.x != 0.0 {
            transform.scale.x = if dir.x < 0.0 { -KIRBY_SCALE } else { KIRBY_SCALE };
        }
        body.force += dir * kirby.speed_force;
        if dir == Vec2::ZERO {
            anim_indices.change(3, 5);
        } else {
            anim_indices.change(6, 8);
        }
    }
}
pub struct KirbyPlugin;

impl Plugin for KirbyPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(OnEnter(GameState::Starting), kirby_spawn_start.in_set(GameStartupSet))
            .add_systems(FixedUpdate, kirby_actions);
    }
}
