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

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

use super::InteractionSnapshot;

pub(super) fn handle_slider(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiSliderData,
    mouse_position: Vec2,
    focused_entity: Option<freecs::Entity>,
    frame_keys: &[(KeyCode, bool)],
) {
    let mut new_value = data.value;
    let mut changed = false;

    if focused_entity == Some(entity) {
        let range = data.max - data.min;
        let step = if data.logarithmic {
            range * 0.01
        } else {
            let natural_step = 10.0_f32.powf((range.log10() - 1.0).floor());
            natural_step.max(0.01)
        };
        for (key, is_pressed) in frame_keys {
            if !is_pressed {
                continue;
            }
            match key {
                KeyCode::ArrowRight | KeyCode::ArrowUp => {
                    new_value = (new_value + step).min(data.max);
                    changed = true;
                }
                KeyCode::ArrowLeft | KeyCode::ArrowDown => {
                    new_value = (new_value - step).max(data.min);
                    changed = true;
                }
                KeyCode::Home => {
                    new_value = data.min;
                    changed = true;
                }
                KeyCode::End => {
                    new_value = data.max;
                    changed = true;
                }
                _ => {}
            }
        }
    }

    if interaction.pressed || interaction.dragging {
        let track_rect = world
            .ui
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            new_value = if data.logarithmic && data.min > 0.0 && data.max > data.min {
                data.min * (data.max / data.min).powf(normalized)
            } else {
                data.min + normalized * (data.max - data.min)
            };
            if (new_value - data.value).abs() > f32::EPSILON {
                changed = true;
            }

            let fill_percent = normalized * 100.0;
            if let Some(fill_node) = world.ui.get_ui_layout_node_mut(data.fill_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
                    fill_node.base_layout.as_mut()
            {
                boundary.position_2 =
                    crate::ecs::ui::units::Rl(Vec2::new(fill_percent, 100.0)).into();
            }

            world.resources.text.cache.set_text(
                data.text_slot,
                format!(
                    "{}{:.prec$}{}",
                    data.prefix,
                    new_value,
                    data.suffix,
                    prec = data.precision
                ),
            );
        }
    }

    if changed && !(interaction.pressed || interaction.dragging) {
        let range = data.max - data.min;
        let normalized = if data.logarithmic && data.min > 0.0 && range > 0.0 {
            ((new_value / data.min).ln() / (data.max / data.min).ln()).clamp(0.0, 1.0)
        } else if range.abs() > f32::EPSILON {
            ((new_value - data.min) / range).clamp(0.0, 1.0)
        } else {
            0.0
        };
        let fill_percent = normalized * 100.0;
        if let Some(fill_node) = world.ui.get_ui_layout_node_mut(data.fill_entity)
            && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
                fill_node.base_layout.as_mut()
        {
            boundary.position_2 = crate::ecs::ui::units::Rl(Vec2::new(fill_percent, 100.0)).into();
        }
        world.resources.text.cache.set_text(
            data.text_slot,
            format!(
                "{}{:.prec$}{}",
                data.prefix,
                new_value,
                data.suffix,
                prec = data.precision
            ),
        );
    }

    if let Some(widget_data) = world.ui.get_ui_slider_mut(entity) {
        widget_data.value = new_value;
        widget_data.changed = changed;
    }
    if changed {
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::SliderChanged {
                entity,
                value: new_value,
            },
        );
    }
}

pub(super) fn handle_splitter(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiSplitterData,
    mouse_position: Vec2,
) {
    use crate::ecs::ui::components::SplitDirection;

    let divider_dragging = world
        .ui
        .get_ui_node_interaction(data.divider_entity)
        .map(|interaction| interaction.dragging)
        .unwrap_or(false);

    if !divider_dragging {
        if let Some(splitter_data) = world.ui.get_ui_splitter_mut(entity) {
            splitter_data.changed = false;
        }
        return;
    }

    let container_rect = world
        .ui
        .get_ui_layout_node(entity)
        .map(|node| node.computed_rect);
    let Some(container_rect) = container_rect else {
        return;
    };

    let new_ratio = match data.direction {
        SplitDirection::Horizontal => {
            let width = container_rect.width();
            if width > 0.0 {
                ((mouse_position.x - container_rect.min.x) / width)
                    .clamp(data.min_ratio, data.max_ratio)
            } else {
                data.ratio
            }
        }
        SplitDirection::Vertical => {
            let height = container_rect.height();
            if height > 0.0 {
                ((mouse_position.y - container_rect.min.y) / height)
                    .clamp(data.min_ratio, data.max_ratio)
            } else {
                data.ratio
            }
        }
    };

    if (new_ratio - data.ratio).abs() < 0.001 {
        return;
    }

    let divider_thickness = 4.0;
    let (
        first_pane_min,
        first_pane_max,
        divider_min,
        divider_max,
        second_pane_min,
        second_pane_max,
    ) = match data.direction {
        SplitDirection::Horizontal => {
            let half = divider_thickness * 0.5;
            (
                crate::ecs::ui::units::Rl(Vec2::new(0.0, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(-half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 0.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(-half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 0.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, 100.0)),
            )
        }
        SplitDirection::Vertical => {
            let half = divider_thickness * 0.5;
            (
                crate::ecs::ui::units::Rl(Vec2::new(0.0, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, -half)),
                crate::ecs::ui::units::Rl(Vec2::new(0.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, -half)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, half)),
                crate::ecs::ui::units::Rl(Vec2::new(0.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, half)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, 100.0)),
            )
        }
    };

    let first_pane = data.first_pane;
    let second_pane = data.second_pane;
    let divider_entity = data.divider_entity;

    if let Some(node) = world.ui.get_ui_layout_node_mut(first_pane)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
            node.base_layout.as_mut()
    {
        boundary.position_1 = first_pane_min.into();
        boundary.position_2 = first_pane_max;
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(divider_entity)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
            node.base_layout.as_mut()
    {
        boundary.position_1 = divider_min;
        boundary.position_2 = divider_max;
    }
    if let Some(node) = world.ui.get_ui_layout_node_mut(second_pane)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
            node.base_layout.as_mut()
    {
        boundary.position_1 = second_pane_min;
        boundary.position_2 = second_pane_max.into();
    }

    if let Some(splitter_data) = world.ui.get_ui_splitter_mut(entity) {
        splitter_data.ratio = new_ratio;
        splitter_data.changed = true;
    }

    world.resources.retained_ui.events_for_active_mut().push(
        crate::ecs::ui::resources::UiEvent::SplitterChanged {
            entity,
            ratio: new_ratio,
        },
    );
}

pub(super) fn handle_breadcrumb(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiBreadcrumbData,
) {
    let mut clicked_segment = None;
    for (index, segment_entity) in data.segment_entities.iter().enumerate() {
        let clicked = world
            .ui
            .get_ui_node_interaction(*segment_entity)
            .map(|interaction| interaction.clicked)
            .unwrap_or(false);
        if clicked {
            clicked_segment = Some(index);
            break;
        }
    }

    if let Some(index) = clicked_segment {
        if let Some(breadcrumb_data) = world.ui.get_ui_breadcrumb_mut(entity) {
            breadcrumb_data.clicked_segment = Some(index);
            breadcrumb_data.changed = true;
        }
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::BreadcrumbClicked {
                entity,
                segment_index: index,
            },
        );
    } else if let Some(breadcrumb_data) = world.ui.get_ui_breadcrumb_mut(entity) {
        breadcrumb_data.clicked_segment = None;
        breadcrumb_data.changed = false;
    }
}

pub(super) fn handle_range_slider(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiRangeSliderData,
    mouse_position: Vec2,
    focused_entity: Option<freecs::Entity>,
    frame_keys: &[(KeyCode, bool)],
) {
    let mut low_value = data.low_value;
    let mut high_value = data.high_value;
    let mut changed = false;
    let mut active_thumb = data.active_thumb;

    let range = data.max - data.min;
    if range.abs() < f32::EPSILON {
        if let Some(widget_data) = world.ui.get_ui_range_slider_mut(entity) {
            widget_data.changed = false;
        }
        return;
    }

    if interaction.pressed {
        let track_rect = world
            .ui
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            let mouse_value = data.min + normalized * range;

            let dist_to_low = (mouse_value - low_value).abs();
            let dist_to_high = (mouse_value - high_value).abs();
            active_thumb = Some(if dist_to_low <= dist_to_high {
                crate::ecs::ui::components::RangeSliderThumb::Low
            } else {
                crate::ecs::ui::components::RangeSliderThumb::High
            });
        }
    }

    if (interaction.pressed || interaction.dragging)
        && let Some(thumb) = active_thumb
    {
        let track_rect = world
            .ui
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            let new_value = data.min + normalized * range;

            match thumb {
                crate::ecs::ui::components::RangeSliderThumb::Low => {
                    low_value = new_value.min(high_value);
                }
                crate::ecs::ui::components::RangeSliderThumb::High => {
                    high_value = new_value.max(low_value);
                }
            }

            if (low_value - data.low_value).abs() > f32::EPSILON
                || (high_value - data.high_value).abs() > f32::EPSILON
            {
                changed = true;
            }

            let low_n = ((low_value - data.min) / range).clamp(0.0, 1.0);
            let high_n = ((high_value - data.min) / range).clamp(0.0, 1.0);

            crate::ecs::ui::widgets::update_range_slider_visuals(
                world,
                &crate::ecs::ui::widgets::RangeSliderVisualUpdate {
                    fill_entity: data.fill_entity,
                    low_thumb: data.low_thumb_entity,
                    high_thumb: data.high_thumb_entity,
                    text_slot: data.text_slot,
                    precision: data.precision,
                    low_normalized: low_n,
                    high_normalized: high_n,
                    low_value,
                    high_value,
                    thumb_half: data.thumb_half_size,
                },
            );
        }
    }

    if !interaction.pressed && !interaction.dragging {
        active_thumb = None;
    }

    if focused_entity == Some(entity) {
        let step = range * 0.01;
        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::Tab => {
                    active_thumb = match active_thumb {
                        Some(crate::ecs::ui::components::RangeSliderThumb::Low) => {
                            Some(crate::ecs::ui::components::RangeSliderThumb::High)
                        }
                        _ => Some(crate::ecs::ui::components::RangeSliderThumb::Low),
                    };
                }
                KeyCode::ArrowLeft | KeyCode::ArrowDown => {
                    let thumb =
                        active_thumb.unwrap_or(crate::ecs::ui::components::RangeSliderThumb::Low);
                    match thumb {
                        crate::ecs::ui::components::RangeSliderThumb::Low => {
                            low_value = (low_value - step).max(data.min).min(high_value);
                        }
                        crate::ecs::ui::components::RangeSliderThumb::High => {
                            high_value = (high_value - step).max(low_value).max(data.min);
                        }
                    }
                    if active_thumb.is_none() {
                        active_thumb = Some(thumb);
                    }
                    changed = true;
                }
                KeyCode::ArrowRight | KeyCode::ArrowUp => {
                    let thumb =
                        active_thumb.unwrap_or(crate::ecs::ui::components::RangeSliderThumb::High);
                    match thumb {
                        crate::ecs::ui::components::RangeSliderThumb::Low => {
                            low_value = (low_value + step).min(high_value).min(data.max);
                        }
                        crate::ecs::ui::components::RangeSliderThumb::High => {
                            high_value = (high_value + step).min(data.max);
                        }
                    }
                    if active_thumb.is_none() {
                        active_thumb = Some(thumb);
                    }
                    changed = true;
                }
                _ => {}
            }
        }

        if changed && !interaction.pressed && !interaction.dragging {
            let low_n = ((low_value - data.min) / range).clamp(0.0, 1.0);
            let high_n = ((high_value - data.min) / range).clamp(0.0, 1.0);
            crate::ecs::ui::widgets::update_range_slider_visuals(
                world,
                &crate::ecs::ui::widgets::RangeSliderVisualUpdate {
                    fill_entity: data.fill_entity,
                    low_thumb: data.low_thumb_entity,
                    high_thumb: data.high_thumb_entity,
                    text_slot: data.text_slot,
                    precision: data.precision,
                    low_normalized: low_n,
                    high_normalized: high_n,
                    low_value,
                    high_value,
                    thumb_half: data.thumb_half_size,
                },
            );
        }
    }

    if let Some(widget_data) = world.ui.get_ui_range_slider_mut(entity) {
        widget_data.low_value = low_value;
        widget_data.high_value = high_value;
        widget_data.changed = changed;
        widget_data.active_thumb = active_thumb;
    }

    if changed {
        world.resources.retained_ui.events_for_active_mut().push(
            crate::ecs::ui::resources::UiEvent::RangeSliderChanged {
                entity,
                low: low_value,
                high: high_value,
            },
        );
    }
}
