use crate::prelude::*;
mod events;
mod handle_basic;
mod handle_context_menu;
mod handle_data_grid;
mod handle_drag_value;
mod handle_input;
mod handle_navigation;
mod handle_panel;
mod handle_rich_text;
mod handle_scroll;
mod handle_slider;
mod handle_text_area;
mod handle_tiles;
mod handle_tree;
mod input_sync;
mod pool_window;
pub mod text_cursor;
mod text_edit;

pub use events::{ui_emit_click_events_system, ui_event_bubble_system};
pub use input_sync::ui_retained_input_sync_system;
pub use text_cursor::measure_text_width;

use nalgebra_glm::Vec2;
use winit::keyboard::KeyCode;

use crate::ecs::world::World;

use handle_basic::{
    handle_checkbox, handle_collapsing_header, handle_radio, handle_spinner, handle_toggle,
};
use handle_context_menu::{close_submenu_defs_state, close_submenu_popups, handle_context_menu};
use handle_data_grid::{
    DataGridContext, handle_command_palette, handle_data_grid, handle_property_grid,
};
use handle_drag_value::handle_drag_value;
use handle_input::{handle_selectable_label, handle_text_input};
use handle_navigation::{
    handle_date_picker, handle_dropdown, handle_menu, handle_multi_select, handle_tab_bar,
};
use handle_panel::{PanelContext, handle_color_picker, handle_color_wheel, handle_panel};
use handle_rich_text::{RichTextEditorContext, handle_rich_text_editor};
use handle_scroll::{handle_scroll_area, handle_virtual_list};
use handle_slider::{handle_breadcrumb, handle_range_slider, handle_slider, handle_splitter};
use handle_text_area::handle_text_area;
use handle_tiles::{TileContainerContext, handle_tile_container};
use handle_tree::{TreeViewContext, handle_modal_dialog, handle_tree_view};

struct TextEditContext<'a> {
    focused_entity: Option<freecs::Entity>,
    frame_chars: &'a [char],
    frame_keys: &'a [(KeyCode, bool)],
    ctrl_held: bool,
    shift_held: bool,
    mouse_position: Vec2,
    current_time: f64,
    dpi_scale: f32,
}

struct InteractionSnapshot {
    clicked: bool,
    pressed: bool,
    dragging: bool,
    hovered: bool,
    double_clicked: bool,
    right_clicked: bool,
    drag_start: Option<Vec2>,
}

fn snapshot_interaction(world: &World, entity: freecs::Entity) -> InteractionSnapshot {
    world
        .ui
        .get_ui_node_interaction(entity)
        .map(|interaction| InteractionSnapshot {
            clicked: interaction.clicked,
            pressed: interaction.pressed,
            dragging: interaction.dragging,
            hovered: interaction.hovered,
            double_clicked: interaction.double_clicked,
            right_clicked: interaction.right_clicked,
            drag_start: interaction.drag_start,
        })
        .unwrap_or(InteractionSnapshot {
            clicked: false,
            pressed: false,
            dragging: false,
            hovered: false,
            double_clicked: false,
            right_clicked: false,
            drag_start: None,
        })
}

pub fn ui_widget_interaction_system(world: &mut World) {
    if !world.resources.retained_ui.enabled {
        return;
    }

    let delta_time = world.resources.retained_ui.timing.delta_time;
    let current_time = world.resources.retained_ui.timing.current_time;
    let mouse_position = crate::ecs::input::access::mouse_for_active(world).position;
    let frame_chars = std::mem::take(&mut world.resources.retained_ui.input.frame_chars);
    let frame_keys = std::mem::take(&mut world.resources.retained_ui.input.frame_keys);
    let ctrl_held = world.resources.retained_ui.input.ctrl_held;
    let shift_held = world.resources.retained_ui.input.shift_held;
    let scroll_delta = world.resources.retained_ui.input.scroll_delta;
    let focused_entity = world
        .resources
        .retained_ui
        .interaction_for_active_mut()
        .focused_entity;
    let mouse_state = crate::ecs::input::access::mouse_for_active(world).state;
    let mouse_just_pressed =
        mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
    let mouse_down = mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
    let dpi_scale = crate::ecs::window::resources::window_scale_factor(world);
    let viewport_size = crate::ecs::window::resources::window_viewport_size(world)
        .map(|(width, height)| Vec2::new(width as f32, height as f32))
        .unwrap_or(Vec2::new(800.0, 600.0));

    let has_text_focus = focused_entity.is_some_and(|fe| {
        world.ui.get_ui_text_input(fe).is_some()
            || world.ui.get_ui_text_area(fe).is_some()
            || world.ui.get_ui_drag_value(fe).is_some()
    });
    if !has_text_focus {
        let bindings = world.resources.retained_ui.groups.shortcuts.clone();
        let alt_held = world
            .resources
            .input
            .keyboard
            .is_key_pressed(KeyCode::AltLeft)
            || world
                .resources
                .input
                .keyboard
                .is_key_pressed(KeyCode::AltRight);
        for &(ref key, pressed) in &frame_keys {
            if !pressed {
                continue;
            }
            for (binding, command_index) in &bindings {
                if binding.key == *key
                    && binding.ctrl == ctrl_held
                    && binding.shift == shift_held
                    && binding.alt == alt_held
                {
                    world.resources.retained_ui.events_for_active_mut().push(
                        crate::ecs::ui::resources::UiEvent::ShortcutTriggered {
                            command_index: *command_index,
                        },
                    );
                }
            }
        }

        let chrome_shortcuts = world.resources.retained_ui.window_chrome.shortcuts.clone();
        for &(ref key, pressed) in &frame_keys {
            if !pressed {
                continue;
            }
            for shortcut in &chrome_shortcuts {
                if shortcut.binding.key == *key
                    && shortcut.binding.ctrl == ctrl_held
                    && shortcut.binding.shift == shift_held
                    && shortcut.binding.alt == alt_held
                {
                    world.resources.retained_ui.events_for_active_mut().push(
                        crate::ecs::ui::resources::UiEvent::ShortcutTriggered {
                            command_index: shortcut.command_index,
                        },
                    );
                }
            }
        }
    }

    let mut panel_rects: Vec<(freecs::Entity, crate::ecs::ui::types::Rect, f32)> = Vec::new();
    let panel_entities: Vec<freecs::Entity> = world
        .ui
        .query_entities(crate::ecs::world::UI_PANEL)
        .collect();
    for entity in &panel_entities {
        if world.ui.get_ui_panel(*entity).is_some()
            && let Some(node) = world.ui.get_ui_layout_node(*entity)
            && node.visible
        {
            panel_rects.push((*entity, node.computed_rect, node.computed_depth));
        }
    }

    let text_edit_ctx = TextEditContext {
        focused_entity,
        frame_chars: &frame_chars,
        frame_keys: &frame_keys,
        ctrl_held,
        shift_held,
        mouse_position,
        current_time,
        dpi_scale,
    };

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_BUTTON)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        if let Some(data) = world.ui.get_ui_button_mut(entity) {
            data.clicked = interaction.clicked;
        }
        if interaction.clicked {
            world
                .resources
                .retained_ui
                .frame
                .events
                .push(crate::ecs::ui::resources::UiEvent::ButtonClicked(entity));
        }
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_SLIDER)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_slider(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_slider(
            world,
            entity,
            &interaction,
            &data,
            mouse_position,
            focused_entity,
            &frame_keys,
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TOGGLE)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_toggle(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_toggle(world, entity, &interaction, &data, delta_time);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_CHECKBOX)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_checkbox(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_checkbox(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_RADIO)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_radio(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_radio(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_SPINNER)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_spinner(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_spinner(world, entity, &data, delta_time);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_COLLAPSING_HEADER)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_collapsing_header(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_collapsing_header(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_SCROLL_AREA)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_scroll_area(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_scroll_area(
            world,
            entity,
            &data,
            scroll_delta,
            mouse_position,
            dpi_scale,
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TAB_BAR)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_tab_bar(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_tab_bar(world, entity, &data, &frame_keys, focused_entity);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TEXT_INPUT)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_text_input(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_text_input(world, entity, &interaction, &data, &text_edit_ctx);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_DROPDOWN)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_dropdown(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_dropdown(
            world,
            entity,
            &interaction,
            &data,
            &frame_keys,
            focused_entity,
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_MENU)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_menu(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_menu(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_PANEL)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_panel(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        let panel_mouse = crate::ecs::input::access::mouse_for_active(world);
        let panel_mouse_position = panel_mouse.position;
        let panel_mouse_state_bits = panel_mouse.state;
        let panel_mouse_just_pressed = panel_mouse_state_bits
            .contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
        let panel_mouse_just_released = panel_mouse_state_bits
            .contains(crate::ecs::input::resources::MouseState::LEFT_JUST_RELEASED);
        let panel_mouse_down =
            panel_mouse_state_bits.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
        let panel_depth = world
            .ui
            .get_ui_layout_node(entity)
            .map(|n| n.computed_depth)
            .unwrap_or(0.0);
        let mouse_occluded = panel_rects
            .iter()
            .any(|(other_entity, other_rect, other_depth)| {
                *other_entity != entity
                    && *other_depth > panel_depth
                    && other_rect.contains(panel_mouse_position)
            });
        handle_panel(
            world,
            entity,
            &data,
            &PanelContext {
                mouse_position: panel_mouse_position,
                mouse_just_pressed: panel_mouse_just_pressed,
                mouse_just_released: panel_mouse_just_released,
                mouse_down: panel_mouse_down,
                dpi_scale,
                viewport_size,
                mouse_occluded,
            },
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_COLOR_PICKER)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_color_picker(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_color_picker(world, entity, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_COLOR_WHEEL)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_color_wheel(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_color_wheel(world, entity, &data, mouse_position);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_SELECTABLE_LABEL)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_selectable_label(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_selectable_label(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_DRAG_VALUE)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_drag_value(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_drag_value(world, entity, &interaction, &data, &text_edit_ctx);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_CONTEXT_MENU)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_context_menu(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_context_menu(world, entity, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TREE_VIEW)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_tree_view(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_tree_view(
            world,
            entity,
            &data,
            &TreeViewContext {
                ctrl_held,
                frame_keys: &frame_keys,
                focused_entity,
                scroll_delta,
                mouse_position,
                dpi_scale,
            },
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_MODAL_DIALOG)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_modal_dialog(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_modal_dialog(world, entity, &data, &frame_keys);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_DATA_GRID)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_data_grid(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_data_grid(
            world,
            entity,
            &data,
            &DataGridContext {
                ctrl_held,
                shift_held,
                frame_keys: &frame_keys,
                mouse_position,
                mouse_just_pressed,
                mouse_down,
            },
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_PROPERTY_GRID)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_property_grid(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_property_grid(
            world,
            entity,
            &data,
            mouse_position,
            mouse_just_pressed,
            mouse_down,
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_COMMAND_PALETTE)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_command_palette(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_command_palette(world, entity, &data, &frame_keys);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_CANVAS)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_canvas(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        if interaction.clicked && data.hit_test_enabled {
            let mouse_pos = world.resources.input.mouse.position;
            if let Some(node) = world.ui.get_ui_layout_node(entity) {
                let local_pos = mouse_pos - node.computed_rect.min;
                for &(command_id, ref rect) in data.command_bounds.iter().rev() {
                    if rect.contains(local_pos) {
                        world.resources.retained_ui.events_for_active_mut().push(
                            crate::ecs::ui::resources::UiEvent::CanvasClicked {
                                entity,
                                command_id,
                                position: local_pos,
                            },
                        );
                        break;
                    }
                }
            }
        }
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TEXT_AREA)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_text_area(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_text_area(world, entity, &interaction, &data, &text_edit_ctx);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_TILE_CONTAINER)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_tile_container(entity) {
            Some(data) => data.clone(),
            None => continue,
        };

        let tile_mouse = crate::ecs::input::access::mouse_for_active(world);
        let tile_mouse_position = tile_mouse.position;
        let tile_mouse_state = tile_mouse.state;
        let tile_mouse_just_pressed =
            tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
        let tile_mouse_just_released =
            tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_RELEASED);
        let tile_mouse_down =
            tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);

        let hovered_entity = world
            .resources
            .retained_ui
            .interaction_for_active()
            .hovered_entity;
        handle_tile_container(
            world,
            entity,
            &data,
            &TileContainerContext {
                mouse_position: tile_mouse_position,
                mouse_just_pressed: tile_mouse_just_pressed,
                mouse_just_released: tile_mouse_just_released,
                mouse_down: tile_mouse_down,
                hovered_entity,
            },
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_VIRTUAL_LIST)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_virtual_list(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_virtual_list(world, entity, &data, dpi_scale, &frame_keys, focused_entity);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_RANGE_SLIDER)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_range_slider(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_range_slider(
            world,
            entity,
            &interaction,
            &data,
            mouse_position,
            focused_entity,
            &frame_keys,
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_BREADCRUMB)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_breadcrumb(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_breadcrumb(world, entity, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_SPLITTER)
        .collect::<Vec<_>>()
    {
        let data = match world.ui.get_ui_splitter(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_splitter(world, entity, &data, mouse_position);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_RICH_TEXT_EDITOR)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_rich_text_editor(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_rich_text_editor(
            world,
            RichTextEditorContext {
                entity,
                interaction: &interaction,
                data: &data,
                focused_entity,
                frame_chars: &frame_chars,
                frame_keys: &frame_keys,
                ctrl_held,
                shift_held,
                mouse_position,
                current_time,
                dpi_scale,
            },
        );
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_MULTI_SELECT)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_multi_select(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_multi_select(world, entity, &interaction, &data);
    }

    for entity in world
        .ui
        .query_entities(crate::ecs::world::UI_DATE_PICKER)
        .collect::<Vec<_>>()
    {
        let interaction = snapshot_interaction(world, entity);
        let data = match world.ui.get_ui_date_picker(entity) {
            Some(data) => data.clone(),
            None => continue,
        };
        handle_date_picker(world, entity, &interaction, &data);
    }

    if scroll_delta.y.abs() > f32::EPSILON && !is_mouse_over_popup(world, mouse_position) {
        close_open_popups(world);
    }

    let error_color = world
        .resources
        .retained_ui
        .theme_state
        .active_theme()
        .error_color;
    let error_entities: Vec<(freecs::Entity, bool)> = world
        .ui
        .query_entities(crate::ecs::world::UI_NODE_INTERACTION)
        .map(|entity| {
            let has_error = world
                .ui
                .get_ui_node_interaction(entity)
                .is_some_and(|interaction| interaction.error_text.is_some());
            (entity, has_error)
        })
        .filter(|(entity, _)| {
            world.ui.get_ui_node_content(*entity).is_some_and(|c| {
                matches!(c, crate::ecs::ui::components::UiNodeContent::Rect { .. })
            })
        })
        .collect();
    for (entity, has_error) in error_entities {
        if has_error
            && let Some(crate::ecs::ui::components::UiNodeContent::Rect {
                border_color,
                border_width,
                ..
            }) = world.ui.get_ui_node_content_mut(entity)
        {
            *border_color = error_color;
            if *border_width < 1.0 {
                *border_width = 1.0;
            }
        }
    }

    world.resources.retained_ui.input.frame_chars = frame_chars;
    world.resources.retained_ui.input.frame_keys = frame_keys;
}

fn is_mouse_over_popup(world: &World, mouse_position: Vec2) -> bool {
    let mut popup_containers: Vec<freecs::Entity> = Vec::new();
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_DROPDOWN)
        .iter(|_, table, idx| {
            let data = &table.ui_dropdown[idx];
            if data.open {
                popup_containers.push(data.popup_container_entity);
            }
        });
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_MENU)
        .iter(|_, table, idx| {
            let data = &table.ui_menu[idx];
            if data.open {
                popup_containers.push(data.popup_container_entity);
            }
        });
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_CONTEXT_MENU)
        .iter(|_, table, idx| {
            let data = &table.ui_context_menu[idx];
            if data.open {
                popup_containers.push(data.popup_entity);
            }
        });
    for popup in popup_containers {
        if let Some(node) = world.ui.get_ui_layout_node(popup)
            && node.computed_rect.contains(mouse_position)
        {
            return true;
        }
    }
    false
}

fn close_open_popups(world: &mut World) {
    let mut open_dropdowns: Vec<(freecs::Entity, freecs::Entity)> = Vec::new();
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_DROPDOWN)
        .iter(|entity, table, idx| {
            let data = &table.ui_dropdown[idx];
            if data.open {
                open_dropdowns.push((entity, data.popup_container_entity));
            }
        });
    for (entity, popup_container) in open_dropdowns {
        if world
            .ui
            .get_ui_layout_node(popup_container)
            .is_some_and(|node| node.visible)
        {
            ui_mark_layout_dirty(world);
        }
        if let Some(node) = world.ui.get_ui_layout_node_mut(popup_container) {
            node.visible = false;
        }
        if let Some(widget_data) = world.ui.get_ui_dropdown_mut(entity) {
            widget_data.open = false;
        }
    }

    let mut open_menus: Vec<(freecs::Entity, freecs::Entity)> = Vec::new();
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_MENU)
        .iter(|entity, table, idx| {
            let data = &table.ui_menu[idx];
            if data.open {
                open_menus.push((entity, data.popup_container_entity));
            }
        });
    for (entity, popup_container) in open_menus {
        if world
            .ui
            .get_ui_layout_node(popup_container)
            .is_some_and(|node| node.visible)
        {
            ui_mark_layout_dirty(world);
        }
        if let Some(node) = world.ui.get_ui_layout_node_mut(popup_container) {
            node.visible = false;
        }
        if let Some(widget_data) = world.ui.get_ui_menu_mut(entity) {
            widget_data.open = false;
        }
    }

    let mut open_context_menus: Vec<(
        freecs::Entity,
        crate::ecs::ui::components::UiContextMenuData,
    )> = Vec::new();
    world
        .ui
        .query()
        .with(crate::ecs::world::UI_CONTEXT_MENU)
        .iter(|entity, table, idx| {
            let data = &table.ui_context_menu[idx];
            if data.open {
                open_context_menus.push((entity, data.clone()));
            }
        });
    for (entity, data) in open_context_menus {
        if let Some(node) = world.ui.get_ui_layout_node_mut(data.popup_entity) {
            node.visible = false;
        }
        close_submenu_popups(world, &data.item_defs);
        if let Some(widget_data) = world.ui.get_ui_context_menu_mut(entity) {
            widget_data.open = false;
            close_submenu_defs_state(&mut widget_data.item_defs);
        }
        world.resources.retained_ui.overlays.active_context_menu = None;
    }
}
