use std::time::Duration;

use bevy::prelude::*;

use crate::{
    core::{
        bubble::{Bubble, bubble_spawn_wave},
        collectible::{COLLECTABLE_SCALE, CollectableType, collectable_spawn},
        game_state::{DispawnOnGameOver, EndGameEvent, EndGameReason, Resetable},
        kirby::Kirby,
        life::Life,
    },
    map::map::{BOTTOM, LEFT, RIGHT, RIGHT_DIR, TOP, UP_DIR},
};

#[derive(Event)]
pub struct NextWaveEvent;

#[derive(Component)]
#[require(DispawnOnGameOver)]
pub struct NextWaveTimer {
    timer: Timer,
}

#[derive(Event)]
struct NextWaveTimerTimeout;

pub struct BubbleType {
    pub color: Color,
    pub move_force: f32,
    pub max_life: f32,
}

pub struct BubbleSplash {
    pub bubble_type: BubbleType,
    pub center: Vec2,
    pub radius: f32,
    pub nb_bubbles: u32,
}

impl BubbleSplash {
    fn new(bubble_type: BubbleType, center: Vec2, radius: f32, nb_bubbles: u32) -> Self {
        BubbleSplash { bubble_type, center, radius, nb_bubbles }
    }
}

const BASE_MOVE_FORCE: f32 = 6000.0;
const BASE_LIFE: f32 = 20.0;

const NORMAL_BUBBLE: BubbleType = BubbleType {
    color: Color::linear_rgb(1.0, 1.0, 1.0),
    move_force: BASE_MOVE_FORCE * 1.0,
    max_life: BASE_LIFE * 1.0,
};

const RED_BUBBLE: BubbleType = BubbleType {
    color: Color::linear_rgb(1.0, 0.0, 0.0),
    move_force: BASE_MOVE_FORCE * 2.0,
    max_life: BASE_LIFE * 0.5,
};

const GREEN_BUBBLE: BubbleType = BubbleType {
    color: Color::linear_rgb(0.0, 1.0, 0.0),
    move_force: BASE_MOVE_FORCE * 0.5,
    max_life: BASE_LIFE * 2.0,
};

#[derive(Resource)]
pub struct BubbleWaves {
    waves: Vec<Vec<BubbleSplash>>,
    collectables: Vec<Vec<(CollectableType, Vec2)>>,
    pub current_wave: usize,
}

fn get_bubble_waves() -> BubbleWaves {
    let return_thing = BubbleWaves {
        waves: vec![
            vec![BubbleSplash::new(NORMAL_BUBBLE, RIGHT, 10.0, 10)],
            vec![BubbleSplash::new(NORMAL_BUBBLE, RIGHT, 10.0, 10), BubbleSplash::new(NORMAL_BUBBLE, TOP, 10.0, 10)],
            vec![BubbleSplash::new(NORMAL_BUBBLE, RIGHT, 10.0, 100), BubbleSplash::new(NORMAL_BUBBLE, TOP, 10.0, 100)],
            vec![
                BubbleSplash::new(NORMAL_BUBBLE, RIGHT, 100.0, 100),
                BubbleSplash::new(NORMAL_BUBBLE, TOP, 100.0, 100),
                BubbleSplash::new(RED_BUBBLE, BOTTOM, 10.0, 20),
            ],
            vec![
                BubbleSplash::new(NORMAL_BUBBLE, RIGHT + TOP, 100.0, 200),
                BubbleSplash::new(RED_BUBBLE, RIGHT + TOP, 100.0, 100),
            ],
            vec![
                BubbleSplash::new(NORMAL_BUBBLE, RIGHT - UP_DIR * 20.0, 100.0, 200),
                BubbleSplash::new(RED_BUBBLE, RIGHT + TOP, 100.0, 100),
                BubbleSplash::new(RED_BUBBLE, LEFT + TOP, 100.0, 100),
            ],
            vec![
                BubbleSplash::new(NORMAL_BUBBLE, RIGHT, 100.0, 200),
                BubbleSplash::new(RED_BUBBLE, RIGHT + UP_DIR * 40.0, 100.0, 100),
                BubbleSplash::new(GREEN_BUBBLE, RIGHT - UP_DIR * 40.0, 100.0, 100),
            ],
            vec![BubbleSplash::new(GREEN_BUBBLE, Vec2::ZERO, 300.0, 1000)],
            vec![
                BubbleSplash::new(RED_BUBBLE, Vec2::ZERO, 300.0, 1000),
                BubbleSplash::new(GREEN_BUBBLE, Vec2::ZERO, 300.0, 1000),
            ],
        ],
        collectables: vec![
            vec![],
            vec![(CollectableType::NewKirby, LEFT)],
            vec![(CollectableType::SpeedBoost, BOTTOM)],
            vec![(CollectableType::ShieldBoost, BOTTOM)],
            vec![(CollectableType::NewKirby, BOTTOM + LEFT)],
            vec![
                (CollectableType::ShieldBoost, TOP),
                (CollectableType::ShieldBoost, TOP + RIGHT_DIR * 40.0),
                (CollectableType::NewKirby, BOTTOM),
            ],
            vec![(CollectableType::NewKirby, LEFT + UP_DIR * 40.0), (CollectableType::NewKirby, LEFT - UP_DIR * 40.0)],
            vec![
                (CollectableType::SpeedBoost, TOP),
                (CollectableType::SpeedBoost, BOTTOM),
                (CollectableType::SpeedBoost, TOP + RIGHT_DIR * 40.0),
                (CollectableType::SpeedBoost, BOTTOM + RIGHT_DIR * 40.0),
                (CollectableType::SpeedBoost, TOP - RIGHT_DIR * 40.0),
                (CollectableType::SpeedBoost, BOTTOM - RIGHT_DIR * 40.0),
                (CollectableType::NewKirby, RIGHT),
            ],
            vec![],
        ],
        current_wave: 0,
    };
    assert!(return_thing.waves.len() == return_thing.collectables.len());
    return return_thing;
}

impl Resetable for BubbleWaves {
    fn reset(&mut self) {
        *self = get_bubble_waves();
    }
}

#[derive(Resource, Default)] // Default is false
pub struct EnnemyWaveLock(bool);

impl Resetable for EnnemyWaveLock {
    fn reset(&mut self) {
        *self = EnnemyWaveLock::default()
    }
}

fn no_ennemy_left(bubble_query: Query<(), With<Bubble>>) -> bool {
    bubble_query.is_empty()
}

#[derive(Component)]
#[require(Sprite, Transform, DispawnOnGameOver)]
struct WarningSign;

fn change_wave(
    mut lock: ResMut<EnnemyWaveLock>,
    bubble_wave: Res<BubbleWaves>,
    mut commands: Commands,
    asset_server: Res<AssetServer>,
) {
    // Beetwen moment where all ennemies are killed and NextWaveTimer trigger the spawn of the new ennemy
    // there is some time, and change_wave will be called every frame during this instance
    // that's why there is an ugly-ass lock
    if lock.0 {
        return;
    }
    lock.0 = true;
    let max_bubble_wave = bubble_wave.waves.len();
    if bubble_wave.current_wave >= max_bubble_wave {
        commands.trigger(EndGameEvent { reason: EndGameReason::Victory });
        return;
    }
    let current_bubble_wave = &bubble_wave.waves[bubble_wave.current_wave];
    for bubble_splash in current_bubble_wave {
        commands.spawn((
            WarningSign,
            Sprite::from_image(asset_server.load("sprites/warning.png")),
            Transform::from_translation(bubble_splash.center.extend(0.0)).with_scale(Vec3::splat(COLLECTABLE_SCALE)),
        ));
    }

    println!("change wave");
    commands.trigger(NextWaveEvent);
    commands.spawn((NextWaveTimer { timer: Timer::new(Duration::from_secs(1), TimerMode::Once) },));
}

fn next_wave_timer_system(
    next_wave_timer_query: Single<(&mut NextWaveTimer, Entity)>,
    time: Res<Time>,
    mut commands: Commands,
) {
    let (mut next_wave_timer, timer_id) = next_wave_timer_query.into_inner();
    next_wave_timer.timer.tick(time.delta());
    if next_wave_timer.timer.is_finished() {
        commands.trigger(NextWaveTimerTimeout);
        commands.entity(timer_id).despawn();
    }
}

fn delete_warning_sign(_: On<NextWaveTimerTimeout>, mut commands: Commands, query: Query<Entity, With<WarningSign>>) {
    for entity in query {
        commands.entity(entity).despawn();
    }
}

fn spawn_wave_ennemy(
    _: On<NextWaveTimerTimeout>,
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut bubble_wave: ResMut<BubbleWaves>,
    mut lock: ResMut<EnnemyWaveLock>,
) {
    let wave = &bubble_wave.waves[bubble_wave.current_wave];
    let collectables = &bubble_wave.collectables[bubble_wave.current_wave];

    bubble_spawn_wave(&mut commands, &asset_server, wave.as_slice());
    for (collectable_type, collectable_pos) in collectables {
        collectable_spawn(&mut commands, &asset_server, *collectable_type, *collectable_pos);
    }
    bubble_wave.current_wave += 1;
    lock.0 = false; // Release lock, ennemy wave is spawned
}

fn heal_kirby(query: Query<&mut Life, With<Kirby>>) {
    for mut kirby_life in query {
        kirby_life.heal(f32::MAX);
    }
}

pub struct WavePlugin;

impl Plugin for WavePlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(get_bubble_waves())
            .insert_resource(EnnemyWaveLock::default())
            .add_systems(Update, (change_wave, heal_kirby).run_if(no_ennemy_left))
            .add_systems(Update, next_wave_timer_system)
            .add_observer(spawn_wave_ennemy)
            .add_observer(delete_warning_sign);
    }
}
