use std::marker::PhantomData;

use bevy::{
    ecs::spawn::SpawnIter, platform::collections::HashMap, prelude::*,
    state::state::FreelyMutableState,
};

use super::{
    ItemPosition, MenuItemInternal, MenuItemType, OnPressAction,
    menus::{Menu, Menus},
};

mod components;

impl<TriggerState, NavState> Plugin for Menus<TriggerState, NavState>
where
    TriggerState: States,
    NavState: States + FreelyMutableState + Copy,
{
    fn build(&self, app: &mut App) {
        app.insert_state(self.start)
            .insert_resource(self.get_store())
            .insert_resource(MenusClosedWhen(self.closed_when))
            .add_observer(build_ui::<NavState>)
            .add_observer(destroy_ui::<NavState>)
            // .add_observer(destroy_ui::<NavStates>)
            .add_systems(
                Update,
                (
                    update_ui_trigger::<NavState>,
                    handle_press_actions::<NavState>.run_if(in_state(self.trigger.clone())),
                ),
            );
    }
}

#[derive(Event)]
struct DestroyUi<S: States>(PhantomData<S>);
#[derive(Event)]
struct BuildUi<S: States>(S);

#[derive(Component)]
struct UiParent<S: States>(PhantomData<S>);
#[derive(Component)]
struct UiComponent;

#[derive(Component)]
struct Action<S: States>(OnPressAction<S>);

impl<T: States, S: States> Menus<T, S> {
    fn get_store(&self) -> MenusStore<S> {
        MenusStore(self.menus.clone())
    }
}

#[derive(Resource)]
struct MenusStore<S: States>(HashMap<S, Menu<S>>);
#[derive(Resource)]
struct MenusClosedWhen<S: States>(S);

fn destroy_ui<S: States>(
    trigger: Trigger<DestroyUi<S>>,
    ui_parent: Query<Entity, With<UiParent<S>>>,
    mut c: Commands,
) {
    trace!("destroy_ui called");
    if let Ok(parent) = ui_parent.single_inner() {
        c.entity(parent).despawn();
    }
}

fn build_ui<S: States>(
    trigger: Trigger<BuildUi<S>>,
    ui_parent: Query<Entity, With<UiParent<S>>>,
    mut c: Commands,
    menus_data: Res<MenusStore<S>>,
) {
    let e = trigger.event();
    let Some(menu_structure) = menus_data.0.get(&e.0) else {
        return;
    };

    let items = menu_structure
        .menu_items
        .clone()
        .into_iter()
        // TODO: implement other item positions
        .filter(|it| ItemPosition::MainView == it.pos);

    c.spawn((
        UiParent(PhantomData::<S>),
        Node {
            width: Val::Percent(100.0),
            height: Val::Percent(100.0),
            align_items: AlignItems::Center,
            justify_content: JustifyContent::Center,
            flex_direction: FlexDirection::Column,
            ..default()
        },
    ))
    .with_children(|parent| {
        for MenuItemInternal { r#type, action, .. } in items {
            let mut e = match r#type {
                MenuItemType::Text(s) => parent.spawn(components::text(s)),
                MenuItemType::Button(s) => parent.spawn(components::button(s)),
            };
            e.insert(UiComponent);

            if let Some(action) = action {
                e.insert(Action(action));
            }
        }
    });
}

fn handle_press_actions<S: States + FreelyMutableState>(
    mut c: Commands,
    mut interaction_query: Query<(&Interaction, &Action<S>), Changed<Interaction>>,
    mut nav_state: ResMut<NextState<S>>,
    closed_when: Res<MenusClosedWhen<S>>,
) {
    for (interaction, Action(action)) in interaction_query {
        if *interaction != Interaction::Pressed {
            continue;
        }
        action.run(&mut c, &mut nav_state, &closed_when.0);
    }
}

fn update_ui_trigger<S: States + PartialEq>(
    mut c: Commands,
    mut trans_reader: EventReader<StateTransitionEvent<S>>,
    menus_data: Res<MenusStore<S>>,
    closed_when: Res<MenusClosedWhen<S>>,
) {
    for trans in trans_reader.read() {
        info!("{trans:?}");

        c.trigger(DestroyUi(PhantomData::<S>));
        if let Some(to_build) = trans.entered.clone() {
            if to_build != closed_when.0 {
                c.trigger(BuildUi(to_build));
            }
        }
    }
}
