use crate::ecs::{EditorMode, EditorWorld};
use crate::systems::mode::{is_descendant_of, nearest_prefab_instance};
use crate::systems::retained_ui::{Action, UiHandles};
use nightshade::ecs::ui::components::*;
use nightshade::ecs::ui::layout_types::FlowDirection;
use nightshade::ecs::ui::state::UiHover;
use nightshade::prelude::*;
use std::collections::{HashMap, HashSet};

const ROW_HEIGHT: f32 = 22.0;
const POOL_SIZE: usize = 64;
const INDENT_PER_DEPTH: f32 = 14.0;
const ARROW_WIDTH: f32 = 14.0;
const ICON_WIDTH: f32 = 18.0;

#[derive(Default, Clone)]
pub struct TreeRow {
    pub entity: Entity,
    pub label: String,
    pub depth: usize,
    pub has_children: bool,
}

#[derive(Default, Clone, Copy)]
pub struct PoolRow {
    pub container: Entity,
    pub indent: Entity,
    pub arrow: Entity,
    pub icon: Entity,
    pub label: Entity,
}

#[derive(Default, Clone)]
pub struct TreePanelHandles {
    pub panel: Entity,
    pub tab_bar: Entity,
    pub scene_container: Entity,
    pub greybox_container: Entity,
    pub filter_input: Entity,
    pub cameras_only_checkbox: Entity,
    pub list: Entity,
    pub pool_rows: Vec<PoolRow>,
    pub all_rows: Vec<TreeRow>,
    pub visible_indices: Vec<usize>,
    pub expanded: HashSet<u32>,
    pub filter_last: String,
    pub filter_lower: String,
    pub cameras_only: bool,
    pub last_signature: u64,
    pub last_visible_start: usize,
    pub selected_entity: Option<Entity>,
    pub selected_entities_len: usize,
    pub context_menu: Entity,
    pub camera_context_menu: Entity,
    pub context_menu_target: Option<Entity>,
}

pub fn build(tree: &mut UiTreeBuilder) -> TreePanelHandles {
    let panel = tree.add_docked_panel_left("entity_tree", "Scene", 280.0);
    let content = super::panel_content(tree, panel);
    let mut filter_input = Entity::default();
    let mut cameras_only_checkbox = Entity::default();
    let mut list = Entity::default();
    let mut tab_bar = Entity::default();
    let mut scene_container = Entity::default();
    let mut greybox_container = Entity::default();
    tree.in_parent(content, |tree| {
        tab_bar = tree.add_tab_bar(&["Scene", "Greybox"], 0);
        scene_container = tree
            .add_node()
            .fill_width()
            .flex_grow(1.0)
            .flow(FlowDirection::Vertical, 0.0, 4.0)
            .entity();
        tree.in_parent(scene_container, |tree| {
            filter_input = tree.add_text_input("Filter entities");
            cameras_only_checkbox = tree.add_checkbox("Cameras only", false);
            list = tree.add_virtual_list(ROW_HEIGHT, POOL_SIZE);
        });
        greybox_container = tree
            .add_node()
            .fill_width()
            .flex_grow(1.0)
            .flow(FlowDirection::Vertical, 0.0, 4.0)
            .with_visible(false)
            .entity();
    });

    let pool_items = tree
        .world_mut()
        .ui
        .get_ui_virtual_list(list)
        .map(|d| d.pool_items.clone())
        .unwrap_or_default();

    let mut pool_rows: Vec<PoolRow> = Vec::with_capacity(pool_items.len());
    for item in pool_items {
        if let Some(node) = tree
            .world_mut()
            .ui
            .get_ui_layout_node_mut(item.container_entity)
        {
            node.flow_layout = Some(FlowLayout {
                direction: FlowDirection::Horizontal,
                padding: 0.0,
                spacing: 4.0,
                alignment: FlowAlignment::Start,
                cross_alignment: FlowAlignment::Center,
                wrap: false,
            });
        }
        let mut indent = Entity::default();
        let mut arrow = Entity::default();
        let mut icon = Entity::default();
        let mut label = Entity::default();
        tree.in_parent(item.container_entity, |tree| {
            indent = tree.add_node().size((0.0).px(), (ROW_HEIGHT).px()).entity();
            arrow = tree
                .add_node()
                .size((ARROW_WIDTH).px(), (ROW_HEIGHT).px())
                .with_text("", ROW_HEIGHT * 0.55)
                .text_center()
                .fg(ThemeColor::Text)
                .entity();
            let icon_set = tree.world_mut().resources.retained_ui.icon_set;
            let lookup = IconLookup::for_set(icon_set);
            icon = tree
                .add_node()
                .size((ICON_WIDTH).px(), (ROW_HEIGHT).px())
                .with_icon(lookup.mesh, ROW_HEIGHT * 0.7)
                .fg(ThemeColor::Text)
                .entity();
            ui_set_text(tree.world_mut(), icon, "");
            label = tree
                .add_node()
                .flow_child(Rl(vec2(0.0, 100.0)))
                .flex_grow(1.0)
                .with_text("", ROW_HEIGHT * 0.65)
                .text_left()
                .with_text_overflow(TextOverflow::Ellipsis)
                .fg(ThemeColor::Text)
                .entity();
        });

        if let Some(content) = tree
            .world_mut()
            .ui
            .get_ui_node_content_mut(item.container_entity)
        {
            *content = UiNodeContent::Rect {
                corner_radius: 2.0,
                border_width: 0.0,
                border_color: vec4(0.0, 0.0, 0.0, 0.0),
            };
        }
        if let Some(weights) = tree
            .world_mut()
            .ui
            .get_ui_state_weights_mut(item.container_entity)
        {
            weights.weights[UiHover::INDEX] = 0.0;
        }

        pool_rows.push(PoolRow {
            container: item.container_entity,
            indent,
            arrow,
            icon,
            label,
        });
    }

    let context_menu = tree.add_context_menu_from_builder(
        ContextMenuBuilder::new()
            .item("Add Child", "")
            .item("Duplicate", "Ctrl+D")
            .item("Delete", "Del")
            .separator()
            .item("Convert similar to instanced", ""),
    );
    let camera_context_menu = tree.add_context_menu_from_builder(
        ContextMenuBuilder::new()
            .item("View Camera", "")
            .item("Add Child", "")
            .item("Duplicate", "Ctrl+D")
            .item("Delete", "Del"),
    );

    TreePanelHandles {
        panel,
        tab_bar,
        scene_container,
        greybox_container,
        filter_input,
        cameras_only_checkbox,
        list,
        pool_rows,
        context_menu,
        camera_context_menu,
        ..Default::default()
    }
}

pub fn sync_tabs(editor_world: &EditorWorld, world: &mut World) {
    let handles = &editor_world.resources.ui_handles.tree;
    let greybox_selected = ui_tab_bar_selected(world, handles.tab_bar) == Some(1);
    ui_set_visible(world, handles.scene_container, !greybox_selected);
    ui_set_visible(world, handles.greybox_container, greybox_selected);
}

pub fn poll(editor_world: &mut EditorWorld, world: &mut World, handles: &UiHandles) {
    let list = handles.tree.list;
    let context_menu = handles.tree.context_menu;
    let camera_context_menu = handles.tree.camera_context_menu;

    let mut clicks: Vec<usize> = Vec::new();
    let mut right_clicks: Vec<(usize, Vec2)> = Vec::new();
    let mut context_menu_clicks: Vec<usize> = Vec::new();
    let mut camera_context_menu_clicks: Vec<usize> = Vec::new();
    for event in ui_events(world) {
        match event {
            UiEvent::VirtualListItemClicked {
                entity, item_index, ..
            } if *entity == list => {
                clicks.push(*item_index);
            }
            UiEvent::VirtualListItemRightClicked {
                entity,
                item_index,
                screen_position,
            } if *entity == list => {
                right_clicks.push((*item_index, *screen_position));
            }
            UiEvent::ContextMenuItemClicked {
                entity, item_index, ..
            } if *entity == context_menu => {
                context_menu_clicks.push(*item_index);
            }
            UiEvent::ContextMenuItemClicked {
                entity, item_index, ..
            } if *entity == camera_context_menu => {
                camera_context_menu_clicks.push(*item_index);
            }
            _ => {}
        }
    }

    let shift_held = world
        .resources
        .input
        .keyboard
        .is_key_pressed(KeyCode::ShiftLeft)
        || world
            .resources
            .input
            .keyboard
            .is_key_pressed(KeyCode::ShiftRight);

    let mut select_actions: Vec<Action> = Vec::new();
    for visible_idx in clicks {
        let Some(row_idx) = editor_world
            .resources
            .ui_handles
            .tree
            .visible_indices
            .get(visible_idx)
            .copied()
        else {
            continue;
        };
        let Some(row) = editor_world
            .resources
            .ui_handles
            .tree
            .all_rows
            .get(row_idx)
            .cloned()
        else {
            continue;
        };
        if row.has_children {
            let entry = editor_world.resources.ui_handles.tree.expanded.clone();
            if entry.contains(&row.entity.id) {
                editor_world
                    .resources
                    .ui_handles
                    .tree
                    .expanded
                    .remove(&row.entity.id);
            } else {
                editor_world
                    .resources
                    .ui_handles
                    .tree
                    .expanded
                    .insert(row.entity.id);
            }
            editor_world.resources.ui_handles.tree.last_visible_start = usize::MAX;
        }
        if shift_held {
            select_actions.push(Action::ToggleSelectEntity(row.entity));
        } else {
            select_actions.push(Action::SelectEntity(row.entity));
        }
    }
    editor_world
        .resources
        .ui_interaction
        .actions
        .extend(select_actions);

    for (visible_idx, position) in right_clicks {
        let Some(row_idx) = editor_world
            .resources
            .ui_handles
            .tree
            .visible_indices
            .get(visible_idx)
            .copied()
        else {
            continue;
        };
        let Some(row) = editor_world
            .resources
            .ui_handles
            .tree
            .all_rows
            .get(row_idx)
            .cloned()
        else {
            continue;
        };
        editor_world.resources.ui_handles.tree.context_menu_target = Some(row.entity);
        let menu = if world.core.entity_has_camera(row.entity) {
            camera_context_menu
        } else {
            context_menu
        };
        ui_show_context_menu(world, menu, position);
    }

    for item_index in context_menu_clicks {
        let Some(target) = editor_world.resources.ui_handles.tree.context_menu_target else {
            continue;
        };
        match item_index {
            0 => editor_world
                .resources
                .ui_interaction
                .actions
                .push(Action::AddEntityChild(target)),
            1 => editor_world
                .resources
                .ui_interaction
                .actions
                .push(Action::DuplicateEntity(target)),
            2 => editor_world
                .resources
                .ui_interaction
                .actions
                .push(Action::DeleteEntity(target)),
            3 => {
                editor_world
                    .resources
                    .ui_interaction
                    .actions
                    .push(Action::SelectEntity(target));
                editor_world
                    .resources
                    .ui_interaction
                    .actions
                    .push(Action::ConvertSimilarToInstanced);
            }
            _ => {}
        }
    }

    for item_index in camera_context_menu_clicks {
        let Some(target) = editor_world.resources.ui_handles.tree.context_menu_target else {
            continue;
        };
        let action = match item_index {
            0 => Some(Action::ViewCamera(target)),
            1 => Some(Action::AddEntityChild(target)),
            2 => Some(Action::DuplicateEntity(target)),
            3 => Some(Action::DeleteEntity(target)),
            _ => None,
        };
        if let Some(action) = action {
            editor_world.resources.ui_interaction.actions.push(action);
        }
    }

    let filter_text = widget::<UiTextInputData>(world, handles.tree.filter_input)
        .map(|data| data.text.clone())
        .unwrap_or_default();
    if filter_text != editor_world.resources.ui_handles.tree.filter_last {
        editor_world.resources.ui_handles.tree.filter_lower = filter_text.to_lowercase();
        editor_world.resources.ui_handles.tree.filter_last = filter_text;
        editor_world.resources.ui_handles.tree.last_visible_start = usize::MAX;
    }

    let cameras_only = widget::<UiCheckboxData>(world, handles.tree.cameras_only_checkbox)
        .map(|data| data.value)
        .unwrap_or(false);
    if cameras_only != editor_world.resources.ui_handles.tree.cameras_only {
        editor_world.resources.ui_handles.tree.cameras_only = cameras_only;
        editor_world.resources.ui_handles.tree.last_signature = 0;
        editor_world.resources.ui_handles.tree.last_visible_start = usize::MAX;
    }
}

pub fn update(editor_world: &mut EditorWorld, world: &mut World) {
    let signature = signature_for(world, editor_world.resources.ui_handles.tree.cameras_only);
    if signature != editor_world.resources.ui_handles.tree.last_signature {
        editor_world.resources.ui_handles.tree.last_signature = signature;
        rebuild_rows(editor_world, world);
        editor_world.resources.ui_handles.tree.last_visible_start = usize::MAX;
    }

    let selected = editor_world.resources.ui.selected_entity;
    let selected_len = editor_world.resources.ui.selected_entities.len();
    if selected != editor_world.resources.ui_handles.tree.selected_entity
        || selected_len != editor_world.resources.ui_handles.tree.selected_entities_len
    {
        editor_world.resources.ui_handles.tree.selected_entity = selected;
        editor_world.resources.ui_handles.tree.selected_entities_len = selected_len;
        editor_world.resources.ui_handles.tree.last_visible_start = usize::MAX;
    }

    refresh_visible_indices(editor_world);
    populate_pool(editor_world, world);
}

fn signature_for(world: &World, cameras_only: bool) -> u64 {
    use std::hash::{Hash, Hasher};
    let mut hasher = std::collections::hash_map::DefaultHasher::new();
    cameras_only.hash(&mut hasher);
    world
        .core
        .query()
        .with(nightshade::ecs::world::LOCAL_TRANSFORM | nightshade::ecs::world::GLOBAL_TRANSFORM)
        .iter(|entity, _, _| {
            entity.id.hash(&mut hasher);
            let parent_id = world
                .core
                .get_parent(entity)
                .and_then(|p| p.0)
                .map(|p| p.id)
                .unwrap_or(0);
            parent_id.hash(&mut hasher);
            let cam = world.core.entity_has_camera(entity);
            cam.hash(&mut hasher);
            let name_hash = world
                .core
                .get_name(entity)
                .map(|n| {
                    let mut h = std::collections::hash_map::DefaultHasher::new();
                    n.0.hash(&mut h);
                    h.finish()
                })
                .unwrap_or(0);
            name_hash.hash(&mut hasher);
        });
    hasher.finish()
}

fn rebuild_rows(editor_world: &mut EditorWorld, world: &World) {
    let cameras_only = editor_world.resources.ui_handles.tree.cameras_only;
    let mode = editor_world.resources.mode.mode;
    editor_world.resources.ui_handles.tree.all_rows.clear();

    let mut all_entities: HashSet<Entity> = HashSet::new();
    world
        .core
        .query()
        .with(nightshade::ecs::world::LOCAL_TRANSFORM | nightshade::ecs::world::GLOBAL_TRANSFORM)
        .iter(|entity, _, _| {
            all_entities.insert(entity);
        });

    let sun_entity = editor_world.resources.sun.sun_entity;
    let viewport_camera = editor_world.resources.camera.camera_entity;
    all_entities.retain(|entity| {
        !editor_world.resources.editor_scene.is_scaffolding(*entity)
            || Some(*entity) == sun_entity
            || Some(*entity) == viewport_camera
    });

    if !cameras_only {
        all_entities.retain(|entity| match mode {
            EditorMode::Object => {
                let nearest = nearest_prefab_instance(world, *entity);
                match nearest {
                    Some(root) => root == *entity,
                    None => true,
                }
            }
            EditorMode::Edit { target } => is_descendant_of(world, *entity, target),
        });
    }

    if cameras_only {
        let mut cameras: Vec<Entity> = all_entities
            .iter()
            .copied()
            .filter(|entity| world.core.entity_has_camera(*entity))
            .collect();
        cameras.sort_by_key(|entity| entity.id);
        let rows = cameras
            .into_iter()
            .map(|entity| TreeRow {
                label: label_for(world, entity),
                entity,
                depth: 0,
                has_children: false,
            })
            .collect();
        editor_world.resources.ui_handles.tree.all_rows = rows;
        return;
    }

    let mut children_map: HashMap<Entity, Vec<Entity>> = HashMap::new();
    let mut roots: Vec<Entity> = Vec::new();
    for entity in &all_entities {
        let parent = world
            .core
            .get_parent(*entity)
            .and_then(|p| p.0)
            .filter(|parent| all_entities.contains(parent));
        match parent {
            Some(parent) => children_map.entry(parent).or_default().push(*entity),
            None => roots.push(*entity),
        }
    }
    roots.sort_by_key(|entity| entity.id);
    for children in children_map.values_mut() {
        children.sort_by_key(|entity| entity.id);
    }

    let mut rows = Vec::new();
    let mut visited: HashSet<Entity> = HashSet::new();
    let mut stack: Vec<(Entity, usize)> = roots.iter().rev().map(|entity| (*entity, 0)).collect();
    while let Some((entity, depth)) = stack.pop() {
        if !visited.insert(entity) {
            continue;
        }
        let has_children = children_map
            .get(&entity)
            .is_some_and(|children| !children.is_empty());
        rows.push(TreeRow {
            label: label_for(world, entity),
            entity,
            depth,
            has_children,
        });
        if let Some(children) = children_map.get(&entity) {
            for child in children.iter().rev() {
                if !visited.contains(child) {
                    stack.push((*child, depth + 1));
                }
            }
        }
    }

    editor_world.resources.ui_handles.tree.all_rows = rows;
}

fn label_for(world: &World, entity: Entity) -> String {
    world
        .core
        .get_name(entity)
        .map(|n| n.0.clone())
        .filter(|n| !n.is_empty())
        .unwrap_or_else(|| format!("Entity {}", entity.id))
}

fn refresh_visible_indices(editor_world: &mut EditorWorld) {
    let tree = &mut editor_world.resources.ui_handles.tree;
    tree.visible_indices.clear();
    let filter = tree.filter_lower.clone();
    let cameras_only = tree.cameras_only;
    let mut skip_until_depth: Option<usize> = None;
    for (idx, row) in tree.all_rows.iter().enumerate() {
        if let Some(depth) = skip_until_depth {
            if row.depth > depth {
                continue;
            } else {
                skip_until_depth = None;
            }
        }
        let collapsed =
            !cameras_only && row.has_children && !tree.expanded.contains(&row.entity.id);
        let matches_filter = filter.is_empty() || row.label.to_lowercase().contains(&filter);
        if matches_filter {
            tree.visible_indices.push(idx);
        }
        if collapsed && filter.is_empty() {
            skip_until_depth = Some(row.depth);
        }
    }
}

fn populate_pool(editor_world: &mut EditorWorld, world: &mut World) {
    let total = editor_world.resources.ui_handles.tree.visible_indices.len();
    let list = editor_world.resources.ui_handles.tree.list;
    ui_virtual_list_set_count(world, list, total);

    let visible_start = world
        .ui
        .get_ui_virtual_list(list)
        .map(|d| d.visible_start)
        .unwrap_or(0);

    let prior_start = editor_world.resources.ui_handles.tree.last_visible_start;
    let icon_set = world.resources.retained_ui.icon_set;
    let icon_lookup = IconLookup::for_set(icon_set);
    let pool_rows = editor_world.resources.ui_handles.tree.pool_rows.clone();
    let pool_size = pool_rows.len();
    let visible_indices = editor_world
        .resources
        .ui_handles
        .tree
        .visible_indices
        .clone();
    let all_rows = editor_world.resources.ui_handles.tree.all_rows.clone();
    let expanded = editor_world.resources.ui_handles.tree.expanded.clone();
    let selected = editor_world.resources.ui.selected_entity;
    let selected_set: std::collections::HashSet<Entity> = editor_world
        .resources
        .ui
        .selected_entities
        .iter()
        .copied()
        .collect();
    let cameras_only = editor_world.resources.ui_handles.tree.cameras_only;
    let filter_active = !editor_world
        .resources
        .ui_handles
        .tree
        .filter_lower
        .is_empty();

    let accent = world
        .resources
        .retained_ui
        .theme_state
        .active_theme()
        .accent_color;
    let primary_bg = vec4(accent.x, accent.y, accent.z, 0.3);
    let secondary_bg = vec4(accent.x, accent.y, accent.z, 0.15);
    let transparent = vec4(0.0, 0.0, 0.0, 0.0);

    if visible_start == prior_start && total <= pool_size {
        // Nothing changed.
    }

    for (pool_index, pool) in pool_rows.iter().enumerate() {
        let visible_idx = visible_start + pool_index;
        let row_idx = visible_indices.get(visible_idx).copied();
        let row = row_idx.and_then(|idx| all_rows.get(idx)).cloned();

        if let Some(row) = row {
            if let Some(node) = world.ui.get_ui_layout_node_mut(pool.indent) {
                node.flow_child_size =
                    Some(Ab(vec2(row.depth as f32 * INDENT_PER_DEPTH, ROW_HEIGHT)).into());
            }

            let arrow_glyph = if cameras_only || filter_active || !row.has_children {
                ""
            } else if expanded.contains(&row.entity.id) {
                "\u{25BC}"
            } else {
                "\u{25B6}"
            };
            ui_set_text(world, pool.arrow, arrow_glyph);

            let icon_glyph = if world.core.entity_has_camera(row.entity) {
                Some(icon_lookup.videocam)
            } else if world.core.entity_has_light(row.entity) {
                Some(icon_lookup.lightbulb)
            } else if world.core.entity_has_render_mesh(row.entity) {
                Some(icon_lookup.mesh)
            } else {
                None
            };
            match icon_glyph {
                Some(glyph) => ui_set_icon(world, pool.icon, glyph),
                None => ui_set_text(world, pool.icon, ""),
            }

            ui_set_text(world, pool.label, &row.label);

            let bg = if Some(row.entity) == selected {
                primary_bg
            } else if selected_set.contains(&row.entity) {
                secondary_bg
            } else {
                transparent
            };
            if let Some(color) = world.ui.get_ui_node_color_mut(pool.container) {
                color.colors[UiBase::INDEX] = Some(bg);
            }
        } else {
            ui_set_text(world, pool.arrow, "");
            ui_set_text(world, pool.label, "");
            ui_set_text(world, pool.icon, "");
        }
    }

    editor_world.resources.ui_handles.tree.last_visible_start = visible_start;
    ui_mark_render_dirty(world);
}
