use crate::ecs::ui::state::UiStateTrait as _;
use winit::keyboard::KeyCode;

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

use super::InteractionSnapshot;

use crate::prelude::*;
fn mouse_inside_rect(world: &World, entity: freecs::Entity) -> bool {
    let mouse_pos = world.resources.input.mouse.position;
    world
        .ui
        .get_ui_layout_node(entity)
        .is_some_and(|node| node.visible && node.computed_rect.contains(mouse_pos))
}

fn mouse_left_just_pressed(world: &World) -> bool {
    world
        .resources
        .input
        .mouse
        .state
        .contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED)
}

/// True when the user just pressed the left button outside an open popup that
/// is bounded by a single container rect, the signal to dismiss it. The owner
/// widget's own hover is excluded so clicking the trigger does not count as
/// outside.
fn dismiss_popup_on_outside_press(
    world: &World,
    owner_hovered: bool,
    popup_entity: freecs::Entity,
) -> bool {
    !owner_hovered && mouse_left_just_pressed(world) && !mouse_inside_rect(world, popup_entity)
}

pub(super) fn handle_tab_bar(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiTabBarData,
    frame_keys: &[(KeyCode, bool)],
    focused_entity: Option<freecs::Entity>,
) {
    let mut new_selected = data.selected_tab;
    let mut changed = false;

    for (index, tab_entity) in data.tab_entities.iter().enumerate() {
        let tab_clicked = world
            .ui
            .get_ui_node_interaction(*tab_entity)
            .map(|i| i.clicked)
            .unwrap_or(false);
        if tab_clicked && data.selected_tab != index {
            new_selected = index;
            changed = true;
            break;
        }
    }

    let any_tab_focused = data
        .tab_entities
        .iter()
        .any(|tab| focused_entity == Some(*tab));
    if any_tab_focused {
        let tab_count = data.tab_entities.len();
        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::ArrowLeft if new_selected > 0 => {
                    new_selected -= 1;
                    changed = true;
                }
                KeyCode::ArrowRight if new_selected + 1 < tab_count => {
                    new_selected += 1;
                    changed = true;
                }
                _ => {}
            }
        }
    }

    if changed {
        let theme = world.resources.retained_ui.theme_state.active_theme();
        let active_bg = theme.accent_color;
        let inactive_bg = theme.background_color;

        if let Some(old_entity) = data.tab_entities.get(data.selected_tab).copied()
            && let Some(color) = world.ui.get_ui_node_color_mut(old_entity)
        {
            color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(inactive_bg);
        }
        if let Some(new_entity) = data.tab_entities.get(new_selected).copied()
            && let Some(color) = world.ui.get_ui_node_color_mut(new_entity)
        {
            color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(active_bg);
        }
    }

    if let Some(widget_data) = world.ui.get_ui_tab_bar_mut(entity) {
        widget_data.selected_tab = new_selected;
        widget_data.changed = changed;
    }
    if changed {
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::TabChanged {
                entity,
                tab_index: new_selected,
            },
        );
    }
}

pub(super) fn handle_dropdown(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiDropdownData,
    frame_keys: &[(KeyCode, bool)],
    focused_entity: Option<freecs::Entity>,
) {
    let mut selected_index = data.selected_index;
    let mut changed = false;
    let mut open = data.open;
    let mut hovered_index = data.hovered_index;
    let is_theme_dropdown = data.is_theme_dropdown;
    let was_open = data.open;
    let option_count = data.options.len();
    let searchable = data.searchable;
    let filter_input_entity = data.filter_input_entity;
    let mut filtered_indices = data.filtered_indices.clone();
    let mut filter_text = data.filter_text.clone();

    if interaction.clicked {
        open = !open;
    }

    if !open && focused_entity == Some(entity) {
        for &(key, pressed) in frame_keys {
            if pressed
                && (key == KeyCode::ArrowDown || key == KeyCode::Space || key == KeyCode::Enter)
            {
                open = true;
                hovered_index = Some(selected_index);
                break;
            }
        }
    }

    if open && !was_open && searchable {
        if let Some(filter_entity) = filter_input_entity {
            if let Some(input_data) = world.ui.get_ui_text_input_mut(filter_entity) {
                input_data.text.clear();
                input_data.cursor_position = 0;
                input_data.selection_start = None;
                input_data.changed = false;
            }
            if let Some(input_data) = world.ui.get_ui_text_input(filter_entity) {
                world
                    .resources
                    .text
                    .cache
                    .set_text(input_data.text_slot, "");
            }
            world
                .resources
                .retained_ui
                .interaction_for_active_mut()
                .focused_entity = Some(filter_entity);
        }
        filter_text.clear();
        filtered_indices = (0..option_count).collect();
    }

    if open
        && searchable
        && let Some(filter_entity) = filter_input_entity
    {
        let current_filter = world
            .ui
            .get_ui_text_input(filter_entity)
            .map(|input_data| input_data.text.clone())
            .unwrap_or_default();

        if current_filter != filter_text {
            filter_text = current_filter;
            let lower_filter = filter_text.to_lowercase();
            filtered_indices = (0..option_count)
                .filter(|idx| {
                    lower_filter.is_empty()
                        || data.options[*idx].to_lowercase().contains(&lower_filter)
                })
                .collect();
            hovered_index = None;

            for (index, popup_entity) in data.popup_entities.iter().enumerate() {
                let visible = filtered_indices.contains(&index);
                if let Some(node) = world.ui.get_ui_layout_node_mut(*popup_entity) {
                    node.visible = visible;
                }
            }
        }
    }

    if open {
        let visible_count = if searchable {
            filtered_indices.len()
        } else {
            option_count
        };

        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::ArrowDown if visible_count > 0 => {
                    let current_visible_pos = hovered_index
                        .and_then(|hi| {
                            if searchable {
                                filtered_indices.iter().position(|&idx| idx == hi)
                            } else {
                                Some(hi)
                            }
                        })
                        .unwrap_or(0);
                    let next_pos = if current_visible_pos + 1 >= visible_count {
                        0
                    } else {
                        current_visible_pos + 1
                    };
                    hovered_index = Some(if searchable {
                        filtered_indices[next_pos]
                    } else {
                        next_pos
                    });
                }
                KeyCode::ArrowUp if visible_count > 0 => {
                    let current_visible_pos = hovered_index
                        .and_then(|hi| {
                            if searchable {
                                filtered_indices.iter().position(|&idx| idx == hi)
                            } else {
                                Some(hi)
                            }
                        })
                        .unwrap_or(0);
                    let prev_pos = if current_visible_pos == 0 {
                        visible_count.saturating_sub(1)
                    } else {
                        current_visible_pos - 1
                    };
                    hovered_index = Some(if searchable {
                        filtered_indices[prev_pos]
                    } else {
                        prev_pos
                    });
                }
                KeyCode::Enter => {
                    if let Some(idx) = hovered_index
                        && let Some(selected_text) = data.options.get(idx).cloned()
                    {
                        selected_index = idx;
                        changed = true;
                        open = false;
                        world
                            .resources
                            .text
                            .cache
                            .set_text(data.header_text_slot, selected_text);
                    }
                }
                KeyCode::Escape => {
                    open = false;
                }
                _ => {}
            }
        }

        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            if searchable && !filtered_indices.contains(&index) {
                continue;
            }
            let popup_interaction = world.ui.get_ui_node_interaction(*popup_entity);
            let popup_clicked = popup_interaction.map(|i| i.clicked).unwrap_or(false);
            let popup_hovered = popup_interaction.map(|i| i.hovered).unwrap_or(false);

            if popup_hovered {
                hovered_index = Some(index);
            }

            if popup_clicked && let Some(selected_text) = data.options.get(index).cloned() {
                selected_index = index;
                changed = true;
                open = false;

                world
                    .resources
                    .text
                    .cache
                    .set_text(data.header_text_slot, selected_text);
                break;
            }
        }

        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            let is_highlighted = hovered_index == Some(index);
            if let Some(weights) = world.ui.get_ui_state_weights_mut(*popup_entity) {
                weights.weights[crate::ecs::ui::state::UiHover::INDEX] =
                    if is_highlighted { 1.0 } else { 0.0 };
            }
        }

        let mouse_just_pressed = mouse_left_just_pressed(world);
        let filter_hovered = filter_input_entity
            .and_then(|fe| world.ui.get_ui_node_interaction(fe))
            .map(|i| i.hovered)
            .unwrap_or(false);
        let mouse_over_popup = data.popup_entities.iter().any(|popup_entity| {
            world
                .ui
                .get_ui_node_interaction(*popup_entity)
                .map(|i| i.hovered)
                .unwrap_or(false)
        });
        if mouse_just_pressed && !interaction.hovered && !mouse_over_popup && !filter_hovered {
            open = false;
        }
    }

    if searchable && was_open && !open {
        if let Some(filter_entity) = filter_input_entity
            && world
                .resources
                .retained_ui
                .interaction_for_active_mut()
                .focused_entity
                == Some(filter_entity)
        {
            world
                .resources
                .retained_ui
                .interaction_for_active_mut()
                .focused_entity = None;
        }
        for popup_entity in &data.popup_entities {
            if let Some(node) = world.ui.get_ui_layout_node_mut(*popup_entity) {
                node.visible = true;
            }
        }
    }

    if is_theme_dropdown {
        if open && hovered_index != data.hovered_index {
            world
                .resources
                .retained_ui
                .theme_state
                .set_preview(hovered_index);
        }
        if was_open && !open {
            world.resources.retained_ui.theme_state.clear_preview();
        }
        if changed {
            world
                .resources
                .retained_ui
                .theme_state
                .select_theme(selected_index);
        }
    }

    let popup_visible_changed = world
        .ui
        .get_ui_layout_node(data.popup_container_entity)
        .is_some_and(|node| node.visible != open);
    if popup_visible_changed {
        ui_mark_layout_dirty(world);
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(data.popup_container_entity) {
        node.visible = open;
    }

    if let Some(widget_data) = world.ui.get_ui_dropdown_mut(entity) {
        widget_data.selected_index = selected_index;
        widget_data.changed = changed;
        widget_data.open = open;
        widget_data.hovered_index = hovered_index;
        widget_data.filter_text = filter_text;
        widget_data.filtered_indices = filtered_indices;
    }
    if changed {
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::DropdownChanged {
                entity,
                selected_index,
            },
        );
    }
}

pub(super) fn handle_multi_select(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiMultiSelectData,
) {
    let mut selected_indices = data.selected_indices.clone();
    let mut changed = false;
    let mut open = data.open;
    let mut hovered_index = data.hovered_index;
    let popup_container_entity = data.popup_container_entity;
    let popup_entities = data.popup_entities.clone();
    let check_entities = data.check_entities.clone();

    if interaction.clicked {
        open = !open;
    }

    if open {
        for (index, &popup_entity) in popup_entities.iter().enumerate() {
            if let Some(popup_interaction) = world.ui.get_ui_node_interaction(popup_entity) {
                if popup_interaction.clicked {
                    if selected_indices.contains(&index) {
                        selected_indices.remove(&index);
                    } else {
                        selected_indices.insert(index);
                    }
                    changed = true;
                }
                if popup_interaction.hovered {
                    hovered_index = Some(index);
                }
            }
        }

        if dismiss_popup_on_outside_press(world, interaction.hovered, popup_container_entity) {
            open = false;
        }
    }

    let header_text = if selected_indices.is_empty() {
        "None selected".to_string()
    } else if selected_indices.len() == 1 {
        let idx = *selected_indices.iter().next().unwrap();
        data.options.get(idx).cloned().unwrap_or_default()
    } else {
        format!("{} selected", selected_indices.len())
    };
    world
        .resources
        .text
        .cache
        .set_text(data.header_text_slot, &header_text);

    for (index, &check_entity) in check_entities.iter().enumerate() {
        let check_text = if selected_indices.contains(&index) {
            "\u{2713}"
        } else {
            " "
        };
        if let Some(crate::ecs::ui::components::UiNodeContent::Text { text_slot, .. }) =
            world.ui.get_ui_node_content(check_entity)
        {
            let slot = *text_slot;
            world.resources.text.cache.set_text(slot, check_text);
        }
    }

    let multi_select_popup_changed = world
        .ui
        .get_ui_layout_node(popup_container_entity)
        .is_some_and(|node| node.visible != open);
    if multi_select_popup_changed {
        ui_mark_layout_dirty(world);
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(popup_container_entity) {
        node.visible = open;
    }

    if let Some(widget_data) = world.ui.get_ui_multi_select_mut(entity) {
        widget_data.selected_indices = selected_indices.clone();
        widget_data.changed = changed;
        widget_data.open = open;
        widget_data.hovered_index = hovered_index;
    }
    if changed {
        let selected_vec: Vec<usize> = selected_indices.into_iter().collect();
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::MultiSelectChanged {
                entity,
                selected_indices: selected_vec,
            },
        );
    }
}

pub(super) fn handle_date_picker(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiDatePickerData,
) {
    let mut year = data.year;
    let mut month = data.month;
    let mut day = data.day;
    let mut changed = false;
    let mut open = data.open;
    let popup_entity = data.popup_entity;
    let day_entities = data.day_entities.clone();
    let day_text_slots = data.day_text_slots.clone();
    let prev_month_entity = data.prev_month_entity;
    let next_month_entity = data.next_month_entity;
    let month_label_slot = data.month_label_slot;
    let header_text_slot = data.header_text_slot;

    if interaction.clicked {
        open = !open;
    }

    let mut repopulate = false;

    if open {
        if let Some(prev_interaction) = world.ui.get_ui_node_interaction(prev_month_entity)
            && prev_interaction.clicked
        {
            if month == 1 {
                month = 12;
                year -= 1;
            } else {
                month -= 1;
            }
            repopulate = true;
        }

        if let Some(next_interaction) = world.ui.get_ui_node_interaction(next_month_entity)
            && next_interaction.clicked
        {
            if month == 12 {
                month = 1;
                year += 1;
            } else {
                month += 1;
            }
            repopulate = true;
        }

        let first_dow = crate::ecs::ui::widgets::day_of_week(year, month, 1);
        let days = crate::ecs::ui::widgets::days_in_month(year, month);

        for (cell_index, &day_entity) in day_entities.iter().enumerate() {
            if let Some(day_interaction) = world.ui.get_ui_node_interaction(day_entity)
                && day_interaction.clicked
            {
                let cell_u32 = cell_index as u32;
                if cell_u32 >= first_dow && cell_u32 < first_dow + days {
                    let clicked_day = cell_u32 - first_dow + 1;
                    day = clicked_day;
                    changed = true;
                    open = false;
                }
            }
        }

        if dismiss_popup_on_outside_press(world, interaction.hovered, popup_entity) {
            open = false;
        }
    }

    if repopulate {
        let accent_color = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .accent_color;
        crate::ecs::ui::widgets::populate_calendar_grid(
            world,
            year,
            month,
            day,
            &day_text_slots,
            &day_entities,
            accent_color,
        );
        world.resources.text.cache.set_text(
            month_label_slot,
            crate::ecs::ui::widgets::format_month_year(year, month),
        );
    }

    if changed {
        let accent_color = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .accent_color;
        crate::ecs::ui::widgets::populate_calendar_grid(
            world,
            year,
            month,
            day,
            &day_text_slots,
            &day_entities,
            accent_color,
        );
        world
            .resources
            .text
            .cache
            .set_text(header_text_slot, format!("{year:04}-{month:02}-{day:02}"));
    }

    if let Some(node) = world.ui.get_ui_layout_node_mut(popup_entity) {
        node.visible = open;
    }

    if let Some(widget_data) = world.ui.get_ui_date_picker_mut(entity) {
        widget_data.year = year;
        widget_data.month = month;
        widget_data.day = day;
        widget_data.changed = changed;
        widget_data.open = open;
    }
    if changed {
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::DatePickerChanged {
                entity,
                year,
                month,
                day,
            },
        );
    }
}

pub(super) fn handle_menu(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiMenuData,
) {
    let mut clicked_item = None;
    let mut open = data.open;

    if interaction.clicked {
        open = !open;
    }

    if open {
        let mut any_popup_hovered = false;
        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            let popup_interaction = world.ui.get_ui_node_interaction(*popup_entity);
            let popup_clicked = popup_interaction.map(|i| i.clicked).unwrap_or(false);
            if popup_interaction.map(|i| i.hovered).unwrap_or(false) {
                any_popup_hovered = true;
            }
            if popup_clicked {
                clicked_item = Some(index);
                open = false;
                break;
            }
        }

        if mouse_left_just_pressed(world) && !interaction.hovered && !any_popup_hovered {
            open = false;
        }
    }

    let menu_popup_changed = world
        .ui
        .get_ui_layout_node(data.popup_container_entity)
        .is_some_and(|node| node.visible != open);
    if menu_popup_changed {
        ui_mark_layout_dirty(world);
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(data.popup_container_entity) {
        node.visible = open;
    }

    if let Some(widget_data) = world.ui.get_ui_menu_mut(entity) {
        widget_data.clicked_item = clicked_item;
        widget_data.open = open;
    }
    if let Some(item_index) = clicked_item {
        world
            .resources
            .retained_ui
            .frame
            .events
            .push(crate::ecs::ui::resources::UiEvent::MenuItemClicked { entity, item_index });
    }
}
