/// TODO: Sorted list/set
///
use bevy::{prelude::*, window::PrimaryWindow};

pub struct GameUiPlugin;

impl Plugin for GameUiPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            (
                init_ui_nav,
                init_ui_tab,
                // UI lists
                init_ui_list,
                // UI Set
                init_ui_set,
                // Buttons
                init_ui_button,
                manage_button_interaction,
                // Cursor
                manage_cursor,
                // Initialize name labels
                init_name,
            ),
        );
    }
}

fn init_name(
    events: Query<
        (Entity, &Name),
        (
            Added<Name>,
            Or<(
                With<GameUiNav>,
                With<GameUiTab>,
                With<GameUiSet>,
                With<GameUiList>,
                With<GameUiButton>,
            )>,
        ),
    >,
    mut commands: Commands,
) {
    events.iter().for_each(|(entity, name)| {
        commands.entity(entity).with_children(|parent| {
            parent.spawn(
                TextBundle::from_section(name, TextStyle { ..default() }).with_style(Style {
                    top: Val::Px(0.0),
                    left: Val::Px(0.0),
                    ..default()
                }),
            );
        });
    });
}

/// Ui Navigation
#[derive(Debug, Component)]
pub struct GameUiNav;

fn init_ui_nav(events: Query<Entity, Added<GameUiNav>>, mut commands: Commands) {
    events.iter().for_each(|entity| {
        let parent = commands
            .spawn(NodeBundle {
                style: Style {
                    flex_direction: FlexDirection::Row,
                    ..default()
                },
                background_color: BackgroundColor(Color::PINK),
                ..default()
            })
            .id();

        commands
            .entity(entity)
            .insert(NodeBundle {
                style: Style { ..default() },
                ..default()
            })
            .set_parent(parent);
    });
}

/// Ui Tab Element
#[derive(Debug, Component)]
pub struct GameUiTab;

fn init_ui_tab(events: Query<Entity, Added<GameUiTab>>, mut commands: Commands) {
    events.iter().for_each(|entity| {
        let parent = commands
            .spawn(NodeBundle {
                style: Style {
                    flex_direction: FlexDirection::Column,
                    ..default()
                },
                ..default()
            })
            .id();

        commands
            .entity(entity)
            .insert(NodeBundle {
                style: Style {
                    flex_wrap: FlexWrap::Wrap,
                    ..default()
                },
                background_color: BackgroundColor(Color::TEAL),
                ..default()
            })
            .set_parent(parent);
    });
}

/// Describes the state of an element
#[derive(Debug, Component)]
pub enum UiElementState {
    Enabled,
    Disabled,
    Active,
    Error,
}

/// GameUiList for holding ordered collections of objects
#[derive(Debug, Component)]
pub struct GameUiList;

/// Manage UI Lists: lists of UI entities.
fn init_ui_list(events: Query<Entity, Added<GameUiList>>, mut commands: Commands) {
    events.iter().for_each(|entity| {
        commands.entity(entity).insert(NodeBundle {
            style: Style {
                flex_direction: FlexDirection::Column,
                justify_items: JustifyItems::Center,
                border: UiRect::all(Val::Px(2.0)),
                margin: UiRect::all(Val::Px(2.0)),
                padding: UiRect::all(Val::Px(2.0)),
                ..default()
            },
            background_color: BackgroundColor(Color::RED),
            border_color: BorderColor(Color::BLACK),
            ..default()
        });
    });
}

/// GameUiSet Component for holding collections of objects
#[derive(Debug, Component)]
pub struct GameUiSet;

/// Manage UI Sets: collections of UI entities.
fn init_ui_set(events: Query<Entity, Added<GameUiSet>>, mut commands: Commands) {
    events.iter().for_each(|entity| {
        let parent = commands
            .spawn(NodeBundle {
                style: Style {
                    flex_direction: FlexDirection::Column,
                    padding: UiRect::all(Val::Px(5.0)),
                    margin: UiRect::all(Val::Px(5.0)),
                    ..default()
                },
                background_color: BackgroundColor(Color::BLUE),
                border_color: BorderColor(Color::BLACK),
                ..default()
            })
            .id();

        commands
            .entity(entity)
            .insert(NodeBundle {
                style: Style {
                    flex_direction: FlexDirection::Row,
                    flex_wrap: FlexWrap::Wrap,
                    border: UiRect::all(Val::Px(2.0)),
                    margin: UiRect::all(Val::Px(2.0)),
                    padding: UiRect::all(Val::Px(2.0)),
                    ..default()
                },
                background_color: BackgroundColor(Color::BLUE),
                border_color: BorderColor(Color::BLACK),
                ..default()
            })
            .set_parent(parent);
    });
}

/// GameUiButton for interactive elements
#[derive(Debug, Component)]
pub struct GameUiButton;

/// Manage UI Buttons. interactive buttons.
fn init_ui_button(events: Query<Entity, Added<GameUiButton>>, mut commands: Commands) {
    events.iter().for_each(|entity| {
        commands.entity(entity).insert((
            ButtonBundle {
                style: Style {
                    margin: UiRect::all(Val::Px(2.0)),
                    padding: UiRect::all(Val::Px(2.0)),
                    border: UiRect::all(Val::Px(2.0)),
                    ..default()
                },
                background_color: BackgroundColor(Color::GREEN),
                border_color: BorderColor(Color::BLACK),
                ..default()
            },
            UiElementState::Enabled,
        ));
    });
}

/// Manage button style for interactivity
fn manage_button_interaction(
    mut events: Query<
        (&Interaction, &UiElementState, &mut BackgroundColor),
        (Changed<Interaction>, With<Button>),
    >,
) {
    events
        .iter_mut()
        .for_each(|(interaction, state, mut bg_color)| {
            bg_color.0 = match state {
                UiElementState::Enabled => match interaction {
                    Interaction::Pressed => Color::BLACK,
                    Interaction::Hovered => Color::ORANGE,
                    Interaction::None => Color::GREEN,
                },
                UiElementState::Active => Color::PINK,
                UiElementState::Error => Color::RED,
                UiElementState::Disabled => Color::GRAY,
            }
        });
}

/// Manage the cursor icon for better immersion
fn manage_cursor(
    mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
    events: Query<&Interaction, (Changed<Interaction>, With<Button>)>,
) {
    if !events.is_empty() {
        let mut window = primary_window.single_mut();
        window.cursor.icon = events
            .iter()
            .find(|&event| *event != Interaction::None)
            .map_or(CursorIcon::Default, |event| match event {
                Interaction::Hovered | Interaction::Pressed => CursorIcon::Hand,
                Interaction::None => CursorIcon::Help, // Shouldn't be reachable
            });
    }
}
