use crate::ecs::ui::state::UiStateTrait as _;
use nalgebra_glm::{Vec2, Vec4};
use winit::keyboard::KeyCode;

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

use super::snapshot_interaction;

use crate::prelude::*;
pub(super) struct DataGridContext<'a> {
    pub(super) ctrl_held: bool,
    pub(super) shift_held: bool,
    pub(super) frame_keys: &'a [(KeyCode, bool)],
    pub(super) mouse_position: Vec2,
    pub(super) mouse_just_pressed: bool,
    pub(super) mouse_down: bool,
}

pub(super) fn handle_data_grid(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
        grid.sort_changed = false;
        grid.selection_changed = false;
    }

    let mut clicked_row = false;
    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let row_interaction = snapshot_interaction(world, pool_row.row_entity);
        if !row_interaction.clicked {
            continue;
        }
        clicked_row = true;

        let visible_row = data.visible_start + pool_index;
        let effective_row_count = data
            .filtered_indices
            .as_ref()
            .map_or(data.total_rows, |indices| indices.len());
        if visible_row >= effective_row_count {
            continue;
        }
        let data_row = visible_row;

        if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
            grid.focused = true;
            if ctx.ctrl_held {
                if grid.selected_rows.contains(&data_row) {
                    grid.selected_rows.remove(&data_row);
                } else {
                    grid.selected_rows.insert(data_row);
                }
                grid.selection_anchor = Some(data_row);
            } else if ctx.shift_held
                && let Some(anchor) = grid.selection_anchor
            {
                let range_start = anchor.min(data_row);
                let range_end = anchor.max(data_row);
                grid.selected_rows.clear();
                for row in range_start..=range_end {
                    grid.selected_rows.insert(row);
                }
            } else {
                grid.selected_rows.clear();
                grid.selected_rows.insert(data_row);
                grid.selection_anchor = Some(data_row);
            }
            grid.selection_changed = true;
        }
        break;
    }

    if ctx.mouse_just_pressed && !clicked_row {
        let grid_rect = world
            .ui
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = grid_rect {
            let inside = ctx.mouse_position.x >= rect.min.x
                && ctx.mouse_position.x <= rect.max.x
                && ctx.mouse_position.y >= rect.min.y
                && ctx.mouse_position.y <= rect.max.y;
            if !inside && let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
                grid.focused = false;
            }
        }
    }

    for (column_index, header_entity) in data.header_entities.iter().enumerate() {
        if column_index < data.columns.len()
            && data.columns[column_index].sortable
            && snapshot_interaction(world, *header_entity).clicked
        {
            if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
                if grid.sort_column == Some(column_index) {
                    if grid.sort_ascending {
                        grid.sort_ascending = false;
                    } else {
                        grid.sort_column = None;
                    }
                } else {
                    grid.sort_column = Some(column_index);
                    grid.sort_ascending = true;
                }
                grid.sort_changed = true;
            }

            data_grid_update_sort_indicators(world, entity, data);
            break;
        }
    }

    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let row_interaction = snapshot_interaction(world, pool_row.row_entity);
        if !row_interaction.double_clicked {
            continue;
        }

        let visible_row = data.visible_start + pool_index;
        let effective_row_count = data
            .filtered_indices
            .as_ref()
            .map_or(data.total_rows, |indices| indices.len());
        if visible_row >= effective_row_count {
            continue;
        }

        let mouse_x = ctx.mouse_position.x;
        let mut col_found = None;
        for (col_idx, cell_entity) in pool_row.cell_entities.iter().enumerate() {
            if col_idx < data.columns.len()
                && data.columns[col_idx].editable
                && let Some(cell_node) = world.ui.get_ui_layout_node(*cell_entity)
            {
                let cell_rect = cell_node.computed_rect;
                if mouse_x >= cell_rect.min.x && mouse_x <= cell_rect.max.x {
                    col_found = Some(col_idx);
                    break;
                }
            }
        }

        if let Some(column) = col_found {
            ui_data_grid_start_edit(world, entity, visible_row, column);
        }
        break;
    }

    if let Some(editing_input) = data.editing_input_entity
        && data.editing_cell.is_some()
    {
        let focused = world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .focused_entity
            == Some(editing_input);
        let mut commit = false;
        let mut cancel = false;

        for &(key, pressed) in ctx.frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::Enter => commit = true,
                KeyCode::Escape => cancel = true,
                _ => {}
            }
        }

        if !focused && !commit && !cancel {
            commit = true;
        }

        if commit {
            ui_data_grid_stop_edit(world, entity, true);
        } else if cancel {
            ui_data_grid_stop_edit(world, entity, false);
        }
    }

    handle_data_grid_column_resize(world, entity, data, ctx);

    let is_focused = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
        grid.focused
    } else {
        false
    };

    if is_focused && data.total_rows > 0 {
        handle_data_grid_keyboard(world, entity, data, ctx);
    }

    update_data_grid_visibility(world, entity, data);
}

fn data_grid_update_sort_indicators(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
) {
    let (sort_column, sort_ascending) = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
        (grid.sort_column, grid.sort_ascending)
    } else {
        return;
    };

    for (index, text_slot) in data.header_text_slots.iter().enumerate() {
        let base_label = &data.columns[index].label;
        let display = match sort_column {
            Some(col) if col == index => {
                let indicator = if sort_ascending {
                    " \u{25B2}"
                } else {
                    " \u{25BC}"
                };
                format!("{base_label}{indicator}")
            }
            _ => base_label.clone(),
        };
        world.resources.text.cache.set_text(*text_slot, &display);
    }
}

pub(super) fn handle_command_palette(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiCommandPaletteData,
    frame_keys: &[(winit::keyboard::KeyCode, bool)],
) {
    if !data.open {
        if let Some(palette) = world.ui.get_ui_command_palette_mut(entity) {
            palette.executed_command = None;
        }
        return;
    }

    let text_changed = if let Some(input) = world.ui.get_ui_text_input(data.text_input_entity) {
        input.changed
    } else {
        false
    };

    if text_changed {
        let new_text = if let Some(input) = world.ui.get_ui_text_input(data.text_input_entity) {
            input.text.clone()
        } else {
            String::new()
        };

        let filter_lower = new_text.to_lowercase();
        let new_indices: Vec<usize> = data
            .commands
            .iter()
            .enumerate()
            .filter(|(_, cmd)| {
                if filter_lower.is_empty() {
                    return true;
                }
                cmd.label.to_lowercase().contains(&filter_lower)
                    || cmd.category.to_lowercase().contains(&filter_lower)
            })
            .map(|(index, _)| index)
            .collect();

        if let Some(palette) = world.ui.get_ui_command_palette_mut(entity) {
            palette.filter_text = new_text;
            palette.filtered_indices = new_indices;
            palette.selected_index = 0;
        }

        ui_command_palette_rebuild_results(world, entity);
    }

    let filtered_count = data.filtered_indices.len();
    let mut execute_index = None;
    let mut should_close = false;

    for &(key, pressed) in frame_keys {
        if !pressed {
            continue;
        }
        match key {
            KeyCode::ArrowUp => {
                if let Some(palette) = world.ui.get_ui_command_palette_mut(entity) {
                    palette.selected_index = palette.selected_index.saturating_sub(1);
                }
            }
            KeyCode::ArrowDown => {
                if filtered_count > 0
                    && let Some(palette) = world.ui.get_ui_command_palette_mut(entity)
                {
                    palette.selected_index = (palette.selected_index + 1).min(filtered_count - 1);
                }
            }
            KeyCode::Enter if filtered_count > 0 => {
                let sel = if let Some(palette) = world.ui.get_ui_command_palette(entity) {
                    palette.selected_index
                } else {
                    0
                };
                if sel < filtered_count {
                    execute_index = Some(data.filtered_indices[sel]);
                }
            }
            KeyCode::Escape => {
                should_close = true;
            }
            _ => {}
        }
    }

    for (pool_index, &row_entity) in data.result_entities.iter().enumerate() {
        if pool_index < filtered_count {
            let clicked = world
                .ui
                .get_ui_node_interaction(row_entity)
                .map(|i| i.clicked)
                .unwrap_or(false);
            if clicked {
                execute_index = Some(data.filtered_indices[pool_index]);
            }
        }
    }

    let selected_index = if let Some(palette) = world.ui.get_ui_command_palette(entity) {
        palette.selected_index
    } else {
        0
    };

    let accent_color = world
        .resources
        .retained_ui
        .theme_state
        .active_theme()
        .accent_color;

    for (pool_index, &row_entity) in data.result_entities.iter().enumerate() {
        if pool_index < filtered_count {
            let is_selected = pool_index == selected_index;
            let bg = if is_selected {
                Vec4::new(accent_color.x, accent_color.y, accent_color.z, 0.3)
            } else {
                Vec4::new(0.0, 0.0, 0.0, 0.0)
            };
            if let Some(color) = world.ui.get_ui_node_color_mut(row_entity) {
                color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(bg);
            }
        }
    }

    let backdrop_clicked = world
        .ui
        .get_ui_node_interaction(data.backdrop_entity)
        .map(|i| i.clicked)
        .unwrap_or(false);
    if backdrop_clicked {
        should_close = true;
    }

    if execute_index.is_some() {
        should_close = true;
    }

    if should_close {
        if let Some(node) = world.ui.get_ui_layout_node_mut(data.backdrop_entity) {
            node.visible = false;
        }
        if let Some(node) = world.ui.get_ui_layout_node_mut(entity) {
            node.visible = false;
        }
        if let Some(palette) = world.ui.get_ui_command_palette_mut(entity) {
            palette.open = false;
            palette.executed_command = execute_index;
        }
        if world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .focused_entity
            == Some(data.text_input_entity)
        {
            world
                .resources
                .retained_ui
                .interaction_for_active_mut()
                .focused_entity = None;
        }

        if let Some(cmd_index) = execute_index {
            world.resources.retained_ui.events_for_active_mut().push(
                crate::ecs::ui::resources::UiEvent::CommandPaletteExecuted {
                    entity,
                    command_index: cmd_index,
                },
            );
        }
    }
}

pub(super) fn handle_property_grid(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiPropertyGridData,
    mouse_position: Vec2,
    mouse_just_pressed: bool,
    mouse_down: bool,
) {
    let currently_resizing = if let Some(grid) = world.ui.get_ui_property_grid(entity) {
        grid.resize_active
    } else {
        return;
    };

    if currently_resizing {
        world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .requested_cursor = Some(winit::window::CursorIcon::ColResize);

        if mouse_down {
            let (start_x, start_width) = if let Some(grid) = world.ui.get_ui_property_grid(entity) {
                (grid.resize_start_x, grid.resize_start_width)
            } else {
                return;
            };

            let grid_rect = world
                .ui
                .get_ui_layout_node(entity)
                .map(|node| node.computed_rect);
            let grid_width = grid_rect.map(|r| r.width()).unwrap_or(300.0);

            let delta = mouse_position.x - start_x;
            let new_width = (start_width + delta).clamp(30.0, grid_width - 50.0);

            if let Some(grid) = world.ui.get_ui_property_grid_mut(entity) {
                grid.label_width = new_width;
            }

            let (viewport_width, viewport_height) = world
                .resources
                .window
                .cached_viewport_size
                .map(|(w, h)| (w as f32, h as f32))
                .unwrap_or((800.0, 600.0));
            let grid_height = grid_rect.map(|r| r.height()).unwrap_or(0.0);

            for label_entity in &data.label_entities {
                if let Some(node) = world.ui.get_ui_layout_node_mut(*label_entity)
                    && let Some(ref mut child_size) = node.flow_child_size
                {
                    let mut evaluated =
                        child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                            parent_width: grid_width,
                            parent_height: grid_height,
                            viewport_width,
                            viewport_height,
                            font_size: 16.0,
                            absolute_scale: 1.0,
                        });
                    evaluated.x = new_width;
                    *child_size = crate::ecs::ui::units::Ab(evaluated).into();
                }
            }
        } else if let Some(grid) = world.ui.get_ui_property_grid_mut(entity) {
            grid.resize_active = false;
        }
        return;
    }

    let edge_threshold = 5.0;
    let mut near_edge = false;

    for label_entity in &data.label_entities {
        let label_rect = world
            .ui
            .get_ui_layout_node(*label_entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = label_rect {
            let near_right_edge = (mouse_position.x - rect.max.x).abs() < edge_threshold
                && mouse_position.y >= rect.min.y
                && mouse_position.y <= rect.max.y;
            if near_right_edge {
                near_edge = true;
                if mouse_just_pressed {
                    if let Some(grid) = world.ui.get_ui_property_grid_mut(entity) {
                        grid.resize_active = true;
                        grid.resize_start_x = mouse_position.x;
                        grid.resize_start_width = grid.label_width;
                    }
                    return;
                }
                break;
            }
        }
    }

    if near_edge {
        world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .requested_cursor = Some(winit::window::CursorIcon::ColResize);
    }
}

pub(super) fn handle_data_grid_column_resize(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    let currently_resizing = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
        grid.resize_column
    } else {
        None
    };

    if let Some(resize_col) = currently_resizing {
        world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .requested_cursor = Some(winit::window::CursorIcon::ColResize);

        if ctx.mouse_down {
            let (start_x, start_width) = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
                (grid.resize_start_x, grid.resize_start_width)
            } else {
                return;
            };

            let delta = ctx.mouse_position.x - start_x;
            let new_width = (start_width + delta).max(30.0);

            if let Some(grid) = world.ui.get_ui_data_grid_mut(entity)
                && let Some(column) = grid.columns.get_mut(resize_col)
            {
                column.width = new_width;
            }

            let viewport = world
                .resources
                .window
                .cached_viewport_size
                .map(|(w, h)| (w as f32, h as f32))
                .unwrap_or((800.0, 600.0));

            if let Some(header_entity) = data.header_entities.get(resize_col)
                && let Some(node) = world.ui.get_ui_layout_node_mut(*header_entity)
                && let Some(ref mut child_size) = node.flow_child_size
            {
                let mut evaluated = child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                    parent_width: viewport.0,
                    parent_height: viewport.1,
                    viewport_width: viewport.0,
                    viewport_height: viewport.1,
                    font_size: 16.0,
                    absolute_scale: 1.0,
                });
                evaluated.x = new_width;
                *child_size = crate::ecs::ui::units::Ab(evaluated).into();
            }

            for pool_row in &data.pool_rows {
                if let Some(&cell_entity) = pool_row.cell_entities.get(resize_col)
                    && let Some(node) = world.ui.get_ui_layout_node_mut(cell_entity)
                    && let Some(ref mut child_size) = node.flow_child_size
                {
                    let mut evaluated =
                        child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                            parent_width: viewport.0,
                            parent_height: viewport.1,
                            viewport_width: viewport.0,
                            viewport_height: viewport.1,
                            font_size: 16.0,
                            absolute_scale: 1.0,
                        });
                    evaluated.x = new_width;
                    *child_size = crate::ecs::ui::units::Ab(evaluated).into();
                }
            }
        } else if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
            grid.resize_column = None;
        }
        return;
    }

    let edge_threshold = 5.0;
    let mut near_any_edge = false;

    for (column_index, header_entity) in data.header_entities.iter().enumerate() {
        let header_rect = world
            .ui
            .get_ui_layout_node(*header_entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = header_rect {
            let near_right_edge = (ctx.mouse_position.x - rect.max.x).abs() < edge_threshold
                && ctx.mouse_position.y >= rect.min.y
                && ctx.mouse_position.y <= rect.max.y;
            if near_right_edge {
                near_any_edge = true;
                if ctx.mouse_just_pressed {
                    if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
                        grid.resize_column = Some(column_index);
                        grid.resize_start_x = ctx.mouse_position.x;
                        grid.resize_start_width = data.columns[column_index].width;
                    }
                    return;
                }
                break;
            }
        }
    }

    if near_any_edge {
        world
            .resources
            .retained_ui
            .interaction_for_active_mut()
            .requested_cursor = Some(winit::window::CursorIcon::ColResize);
    }
}

pub(super) fn handle_data_grid_keyboard(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    let total_rows = data
        .filtered_indices
        .as_ref()
        .map_or(data.total_rows, |indices| indices.len());
    if total_rows == 0 {
        return;
    }

    let current_anchor = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
        grid.selection_anchor
    } else {
        return;
    };

    let anchor = current_anchor.unwrap_or(0);
    let mut new_anchor = None;

    for &(key, pressed) in ctx.frame_keys {
        if !pressed {
            continue;
        }
        match key {
            KeyCode::ArrowUp => {
                new_anchor = Some(anchor.saturating_sub(1));
            }
            KeyCode::ArrowDown => {
                new_anchor = Some((anchor + 1).min(total_rows - 1));
            }
            KeyCode::PageUp => {
                new_anchor = Some(anchor.saturating_sub(data.pool_size));
            }
            KeyCode::PageDown => {
                new_anchor = Some((anchor + data.pool_size).min(total_rows - 1));
            }
            KeyCode::Home => {
                new_anchor = Some(0);
            }
            KeyCode::End => {
                new_anchor = Some(total_rows - 1);
            }
            KeyCode::KeyA if ctx.ctrl_held => {
                if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
                    grid.selected_rows.clear();
                    for row in 0..total_rows {
                        grid.selected_rows.insert(row);
                    }
                    grid.selection_changed = true;
                }
                return;
            }
            _ => {}
        }
    }

    if let Some(target) = new_anchor {
        if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
            if ctx.shift_held {
                let range_start = anchor.min(target);
                let range_end = anchor.max(target);
                grid.selected_rows.clear();
                for row in range_start..=range_end {
                    grid.selected_rows.insert(row);
                }
            } else {
                grid.selected_rows.clear();
                grid.selected_rows.insert(target);
                grid.selection_anchor = Some(target);
            }
            grid.selection_changed = true;
        }

        let row_height = data.row_height;
        let target_offset = target as f32 * row_height;
        if let Some(scroll_data) = world.ui.get_ui_scroll_area(data.scroll_entity) {
            let scroll_offset = scroll_data.scroll_offset;
            let visible_height = scroll_data.visible_height;
            if target_offset < scroll_offset {
                if let Some(scroll_data) = world.ui.get_ui_scroll_area_mut(data.scroll_entity) {
                    scroll_data.scroll_offset = target_offset;
                }
            } else if target_offset + row_height > scroll_offset + visible_height
                && let Some(scroll_data) = world.ui.get_ui_scroll_area_mut(data.scroll_entity)
            {
                scroll_data.scroll_offset = target_offset + row_height - visible_height;
            }
        }
    }
}

fn update_data_grid_visibility(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
) {
    let scroll_offset = if let Some(scroll_data) = world.ui.get_ui_scroll_area(data.scroll_entity) {
        scroll_data.scroll_offset
    } else {
        0.0
    };

    let effective_row_count = data
        .filtered_indices
        .as_ref()
        .map_or(data.total_rows, |indices| indices.len());

    let window = super::pool_window::compute(
        scroll_offset,
        data.row_height,
        effective_row_count,
        data.pool_size,
    );
    let visible_start = window.visible_start;

    if let Some(node) = world.ui.get_ui_layout_node_mut(data.top_spacer) {
        node.flow_child_size =
            Some(crate::ecs::ui::units::Ab(nalgebra_glm::Vec2::new(0.0, window.top_height)).into());
    }

    if let Some(node) = world.ui.get_ui_layout_node_mut(data.bottom_spacer) {
        node.flow_child_size = Some(
            crate::ecs::ui::units::Ab(nalgebra_glm::Vec2::new(0.0, window.bottom_height)).into(),
        );
    }

    let selected_rows = if let Some(grid) = world.ui.get_ui_data_grid(entity) {
        grid.selected_rows.clone()
    } else {
        std::collections::HashSet::new()
    };

    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let visible_row = visible_start + pool_index;
        let visible = visible_row < effective_row_count;

        if let Some(node) = world.ui.get_ui_layout_node_mut(pool_row.row_entity) {
            node.visible = visible;
        }

        if visible {
            let is_selected = selected_rows.contains(&visible_row);
            if let Some(weights) = world.ui.get_ui_state_weights_mut(pool_row.row_entity) {
                weights.weights[crate::ecs::ui::state::UiSelected::INDEX] =
                    if is_selected { 1.0 } else { 0.0 };
            }
        }
    }

    if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
        grid.visible_start = visible_start;
    }
}
