use bevy::app::AppExit;
use bevy::audio::AudioPlugin;
use bevy::prelude::*;
use bevy::state::app::StatesPlugin;
use bevy_asset_loader::prelude::*;

#[test]
fn main() {
    let mut app = App::new();

    app.add_plugins((
        MinimalPlugins,
        AssetPlugin::default(),
        AudioPlugin::default(),
        StatesPlugin,
    ));
    app.init_state::<Prepare>().init_state::<Game>();
    app.add_loading_state(
        LoadingState::new(Game::Booting)
            .continue_to_state(Game::Loading)
            .load_collection::<GameStateCollection>(),
    )
    .add_loading_state(
        LoadingState::new(Prepare::Loading)
            .continue_to_state(Prepare::Finalize)
            .load_collection::<LoadingStateCollection>(),
    )
    .add_systems(Update, (quit.run_if(in_state(Game::Play)), timeout))
    .add_systems(
        OnEnter(Game::Loading),
        (go_to_loading_loading, probe_game_state),
    )
    .add_systems(OnEnter(Prepare::Finalize), go_to_game_play_loading_done)
    .add_systems(OnEnter(Game::Play), probe_loading_state)
    .run();
}

#[derive(Clone, Copy, Debug, States, Default, PartialEq, Eq, Hash)]
enum Game {
    #[default]
    Booting,
    Loading,
    Play,
}

#[derive(Clone, Copy, Debug, States, Default, PartialEq, Eq, Hash)]
enum Prepare {
    #[default]
    Done,
    Loading,
    Finalize,
}

#[derive(Resource, AssetCollection)]
pub struct GameStateCollection {
    #[asset(path = "audio/background.ogg")]
    _background: Handle<AudioSource>,
}

fn probe_game_state(_res: Res<GameStateCollection>) {}

#[derive(Resource, AssetCollection)]
pub struct LoadingStateCollection {
    #[asset(path = "audio/yippee.ogg")]
    _yippee: Handle<AudioSource>,
}

fn probe_loading_state(_res: Res<LoadingStateCollection>) {}

fn go_to_loading_loading(mut state: ResMut<NextState<Prepare>>) {
    state.set(Prepare::Loading);
}

fn go_to_game_play_loading_done(
    mut game_state: ResMut<NextState<Game>>,
    mut loading_state: ResMut<NextState<Prepare>>,
) {
    game_state.set(Game::Play);
    loading_state.set(Prepare::Done);
}

fn timeout(time: Res<Time>) {
    if time.elapsed_secs_f64() > 30. {
        panic!("The app did not finish in 30 seconds");
    }
}

fn quit(mut exit: MessageWriter<AppExit>) {
    info!("Everything fine, quitting the app");
    exit.write(AppExit::Success);
}
