use super::*;

pub fn authoring_inspector_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    if !world
        .get_entity(entity)
        .is_ok_and(|entity_ref| entity_ref.contains::<LevelObject>())
    {
        return;
    }

    let present = present_component_type_names(world, entity);
    let ordered = if let Some(order) = world.get::<InspectorOrder>(entity) {
        world
            .resource::<EditorComponentRegistry>()
            .ordered_components(order, &present)
    } else {
        present
    };
    for type_name in ordered {
        let inspector = world
            .resource::<EditorComponentRegistry>()
            .inspector(type_name);
        if let Some(inspector) = inspector {
            ui.push_id(("component_inspector", type_name), |ui| {
                inspector(world, ui, entity);
            });
        } else {
            ui.colored_label(
                crate::ui::theme::ERROR,
                format!("No Inspector renderer registered for {type_name}"),
            );
        }
    }
}

pub(crate) fn register_builtin_component_inspectors(world: &mut World) {
    let registrations: &[(&str, crate::ui::component_registry::ComponentInspectorFn)] = &[
        (
            COMPONENT_ANIMATION_CONTROLLER_DESC,
            crate::ui::animation_inspector::animation_controller_inspector_ui,
        ),
        (COMPONENT_STATIC_MESH_RENDERER, static_mesh_renderer_ui),
        (COMPONENT_SKINNED_MESH_RENDERER, skinned_mesh_renderer_ui),
        (COMPONENT_BRUSH_DESC, brush_editor_ui),
        (COMPONENT_TERRAIN_DESC, terrain_editor_ui),
        (COMPONENT_PRIMITIVE, primitive_editor_ui),
        (COMPONENT_MATERIAL_DESC, material_editor_ui),
        (COMPONENT_LIGHT_DESC, light_editor_ui),
        (
            COMPONENT_AUDIO_SOURCE_DESC,
            crate::ui::audio_inspector::audio_source_inspector_ui,
        ),
        (
            COMPONENT_AUDIO_LISTENER_DESC,
            crate::ui::audio_inspector::audio_listener_inspector_ui,
        ),
        (COMPONENT_RIGID_BODY_DESC, rigid_body_editor_ui),
        (COMPONENT_COLLIDER_DESC, collider_editor_ui),
        (COMPONENT_PHYSICS_BODY, physics_editor_ui),
        (COMPONENT_PLAYER_SPAWN, player_spawn_ui),
        (COMPONENT_WEAPON_SPAWN, weapon_spawn_ui),
        (COMPONENT_TRIGGER_VOLUME, trigger_volume_ui),
        (COMPONENT_TEAM_SPAWN, team_spawn_ui),
        (COMPONENT_OBJECTIVE_MARKER, objective_ui),
        (COMPONENT_PREFAB_INSTANCE, prefab_instance_ui),
        (
            COMPONENT_POST_PROCESS_VOLUME,
            crate::ui::post_process_volume_ui::post_process_volume_inspector_ui,
        ),
        (COMPONENT_PROJECT_SUN, project_sun_ui),
        (
            COMPONENT_NAVIGATION_BOUNDS,
            crate::ui::navigation_inspector::navigation_bounds_inspector_ui,
        ),
        (
            COMPONENT_NAVIGATION_OBSTACLE,
            crate::ui::navigation_inspector::navigation_obstacle_inspector_ui,
        ),
        (
            COMPONENT_NAVIGATION_AREA,
            crate::ui::navigation_inspector::navigation_area_inspector_ui,
        ),
        (
            COMPONENT_NAVIGATION_LINK,
            crate::ui::navigation_inspector::navigation_link_inspector_ui,
        ),
    ];
    let mut registry = world.resource_mut::<EditorComponentRegistry>();
    for &(type_name, inspector) in registrations {
        registry
            .register_inspector_for_existing(type_name, inspector)
            .unwrap_or_else(|error| panic!("built-in Inspector registration failed: {error}"));
    }
}

pub(crate) fn add_component_footer(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    ui.add_space(4.0);
    let button_response = egui::Frame::new()
        .fill(WIDGET_BG)
        .stroke(egui::Stroke::new(1.0_f32, BORDER))
        .corner_radius(egui::CornerRadius::same(4))
        .inner_margin(egui::Margin::symmetric(8, 6))
        .show(ui, |ui| {
            let open_for_entity = add_component_picker_open_for(world, entity);
            let label = if open_for_entity {
                "Close Add Component"
            } else {
                "+ Add Component"
            };
            if ui.button(label).clicked() {
                if let Some(mut state) = world.get_resource_mut::<InspectorPanelState>() {
                    if open_for_entity {
                        state.add_component_open = false;
                        state.add_component_target = None;
                    } else {
                        state.add_component_open = true;
                        state.add_component_focus_search = true;
                        state.add_component_scroll_selected = true;
                        state.add_component_selected_index = 0;
                        state.add_component_target = Some(entity);
                        state.add_component_search.clear();
                    }
                }
            }
        });

    if add_component_picker_open_for(world, entity) {
        add_component_picker_shelf(world, ui, entity, button_response.response.rect);
    }
}

pub(super) fn add_component_picker_open_for(world: &World, entity: Entity) -> bool {
    world
        .get_resource::<InspectorPanelState>()
        .is_some_and(|state| state.add_component_open && state.add_component_target == Some(entity))
}

pub(super) fn shelf_list_max_height(visible_space: f32) -> f32 {
    (visible_space - 108.0).clamp(56.0, 320.0)
}

pub(super) fn add_component_picker_shelf(
    world: &mut World,
    ui: &mut egui::Ui,
    target: Entity,
    anchor: egui::Rect,
) {
    if world.get_entity(target).is_err() {
        if let Some(mut state) = world.get_resource_mut::<InspectorPanelState>() {
            state.add_component_open = false;
            state.add_component_target = None;
        }
        return;
    }

    let descriptors = world
        .resource::<EditorComponentRegistry>()
        .descriptors
        .clone();

    let visible = ui.clip_rect().intersect(ui.ctx().content_rect());
    let space_above = (anchor.min.y - visible.top()).max(0.0);
    let space_below = (visible.bottom() - anchor.max.y).max(0.0);
    let direction = if space_below >= space_above {
        AddComponentShelfDirection::Down
    } else {
        AddComponentShelfDirection::Up
    };
    let visible_space = match direction {
        AddComponentShelfDirection::Up => space_above,
        AddComponentShelfDirection::Down => space_below,
    };
    let list_max_height = shelf_list_max_height(visible_space);
    let shelf_height = (list_max_height + 108.0).min((visible_space - 4.0).max(96.0));
    let x = anchor
        .min
        .x
        .clamp(visible.left(), visible.right() - anchor.width());
    let y = match direction {
        AddComponentShelfDirection::Up => anchor.min.y - shelf_height - 4.0,
        AddComponentShelfDirection::Down => anchor.max.y + 4.0,
    }
    .clamp(
        visible.top(),
        (visible.bottom() - shelf_height).max(visible.top()),
    );
    let shelf_width = anchor.width().max(260.0).min(visible.width());

    egui::Area::new(egui::Id::new(("add_component_shelf", target)))
        .order(egui::Order::Foreground)
        .fixed_pos(egui::pos2(x, y))
        .show(ui.ctx(), |ui| {
            ui.set_width(shelf_width);
            add_component_picker_shelf_contents(world, ui, target, &descriptors, list_max_height);
        });
}

pub(super) fn add_component_picker_shelf_contents(
    world: &mut World,
    ui: &mut egui::Ui,
    target: Entity,
    descriptors: &[EditorComponentDescriptor],
    list_max_height: f32,
) {
    egui::Frame::new()
        .fill(PANEL_BG_DARK)
        .stroke(egui::Stroke::new(1.0_f32, BORDER))
        .corner_radius(egui::CornerRadius::same(4))
        .inner_margin(egui::Margin::symmetric(8, 8))
        .show(ui, |ui| {
            ui.horizontal(|ui| {
                ui.label(panel_heading("Add Component"));
                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                    if icon_button_small(ui, icons::X, "Close Add Component").clicked() {
                        let mut state = world.resource_mut::<InspectorPanelState>();
                        state.add_component_open = false;
                        state.add_component_target = None;
                    }
                });
            });
            ui.add_space(4.0);

            let mut search_input = world
                .resource::<InspectorPanelState>()
                .add_component_search
                .clone();
            let search_response = ui.add(
                egui::TextEdit::singleline(&mut search_input)
                    .hint_text("Search components...")
                    .desired_width(f32::INFINITY),
            );
            if search_response.changed() {
                let mut state = world.resource_mut::<InspectorPanelState>();
                state.add_component_search = search_input.clone();
                state.add_component_scroll_selected = true;
                state.add_component_selected_index = 0;
            }
            if world
                .resource::<InspectorPanelState>()
                .add_component_focus_search
            {
                search_response.request_focus();
                world
                    .resource_mut::<InspectorPanelState>()
                    .add_component_focus_search = false;
            }

            let search = search_input.to_lowercase();
            let filtered = filtered_component_descriptors(descriptors, &search);
            {
                let mut state = world.resource_mut::<InspectorPanelState>();
                if !filtered.is_empty() {
                    state.add_component_selected_index =
                        state.add_component_selected_index.min(filtered.len() - 1);
                } else {
                    state.add_component_selected_index = 0;
                }
            }

            if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::ArrowDown))
                && !filtered.is_empty()
            {
                let mut state = world.resource_mut::<InspectorPanelState>();
                state.add_component_selected_index =
                    (state.add_component_selected_index + 1) % filtered.len();
                state.add_component_scroll_selected = true;
            }
            if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::ArrowUp))
                && !filtered.is_empty()
            {
                let mut state = world.resource_mut::<InspectorPanelState>();
                state.add_component_selected_index = if state.add_component_selected_index == 0 {
                    filtered.len() - 1
                } else {
                    state.add_component_selected_index - 1
                };
                state.add_component_scroll_selected = true;
            }
            if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Escape)) {
                let mut state = world.resource_mut::<InspectorPanelState>();
                state.add_component_open = false;
                state.add_component_target = None;
            }
            if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Enter)) {
                let selected_index = world
                    .resource::<InspectorPanelState>()
                    .add_component_selected_index;
                if let Some(descriptor) = filtered.get(selected_index) {
                    let add_state = component_add_state(world, target, descriptor, descriptors);
                    if add_state.addable {
                        insert_registered_component(world, target, descriptor.type_name);
                        let mut state = world.resource_mut::<InspectorPanelState>();
                        state.add_component_open = false;
                        state.add_component_target = None;
                    }
                }
            }

            ui.separator();
            if filtered.is_empty() {
                ui.label(egui::RichText::new("No matching components").color(TEXT_DIM));
                return;
            }

            egui::ScrollArea::vertical()
                .max_height(list_max_height)
                .show(ui, |ui| {
                    let (selected_index, scroll_selected) = {
                        let state = world.resource::<InspectorPanelState>();
                        (
                            state.add_component_selected_index,
                            state.add_component_scroll_selected,
                        )
                    };
                    let mut last_category = None;
                    for (index, descriptor) in filtered.iter().enumerate() {
                        if last_category != Some(descriptor.category) {
                            if last_category.is_some() {
                                ui.separator();
                            }
                            ui.label(panel_heading(component_category_label(descriptor.category)));
                            last_category = Some(descriptor.category);
                        }

                        let add_state = component_add_state(world, target, descriptor, descriptors);
                        let selected = index == selected_index;
                        let row = ui
                            .horizontal(|ui| {
                                ui.label(phosphor_icon_text(descriptor.icon, 14.0).color(TEXT_DIM));
                                ui.add_enabled(
                                    add_state.addable,
                                    egui::Button::selectable(selected, descriptor.display_name),
                                )
                            })
                            .inner
                            .on_hover_text(component_hover_text(descriptor, &add_state));
                        if selected && scroll_selected {
                            row.scroll_to_me(Some(egui::Align::Center));
                        }
                        if row.clicked() {
                            world
                                .resource_mut::<InspectorPanelState>()
                                .add_component_selected_index = index;
                            if add_state.addable {
                                insert_registered_component(world, target, descriptor.type_name);
                                let mut state = world.resource_mut::<InspectorPanelState>();
                                state.add_component_open = false;
                                state.add_component_target = None;
                            }
                        }
                    }
                });
            world
                .resource_mut::<InspectorPanelState>()
                .add_component_scroll_selected = false;
        });
}

pub(super) fn filtered_component_descriptors<'a>(
    descriptors: &'a [EditorComponentDescriptor],
    search: &str,
) -> Vec<&'a EditorComponentDescriptor> {
    [
        EditorComponentCategory::Authoring,
        EditorComponentCategory::Rendering,
        EditorComponentCategory::Animation,
        EditorComponentCategory::Audio,
        EditorComponentCategory::Navigation,
        EditorComponentCategory::Physics,
        EditorComponentCategory::Gameplay,
        EditorComponentCategory::Volumes,
    ]
    .into_iter()
    .flat_map(|category| {
        descriptors.iter().filter(move |descriptor| {
            descriptor.addable
                && !descriptor.hidden
                && descriptor.category == category
                && descriptor_matches_search(descriptor, search)
        })
    })
    .collect()
}
