use nightshade::ecs::generational_registry::registry_entry_by_name_mut;
use nightshade::ecs::material::resources::material_registry_insert;
use nightshade::prelude::serde::{Deserialize, Serialize};
use nightshade::prelude::strum::{Display, EnumIter};
use nightshade::prelude::*;

#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, EnumIter, Display, Serialize, Deserialize,
)]
#[serde(crate = "nightshade::prelude::serde")]
#[strum(crate = "nightshade::prelude::strum")]
pub enum WidgetKind {
    #[default]
    Camera,
    Materials,
    Web,
    #[strum(serialize = "Asset Browser")]
    AssetBrowser,
    #[strum(serialize = "Scene Browser")]
    SceneBrowser,
}

impl WidgetKind {
    pub fn title(&self) -> String {
        self.to_string()
    }
}

pub struct SceneInfo {
    pub name: String,
    pub entity_count: usize,
    pub is_active: bool,
}

#[derive(Default)]
pub struct SceneBrowserContext {
    pub scenes: Vec<SceneInfo>,
    pub has_project: bool,
}

#[derive(Clone)]
pub enum SceneAction {
    SwitchTo(String),
    Import(std::path::PathBuf),
    Delete(String),
}

pub struct WidgetContext<'a> {
    pub world: *mut World,
    pub selected_viewport_tile: &'a mut Option<nightshade::prelude::egui_tiles::TileId>,
    pub viewport_textures: &'a Vec<egui::TextureId>,
    pub current_tile_id: nightshade::prelude::egui_tiles::TileId,
    pub current_project_path: Option<&'a std::path::PathBuf>,
    #[cfg(not(target_arch = "wasm32"))]
    pub web_widget_rects: &'a mut Vec<(String, String, egui::Rect)>,
    pub scene_browser: &'a SceneBrowserContext,
    pub scene_actions: &'a mut Vec<SceneAction>,
}

pub trait Widget {
    fn title(&self) -> String;
    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext);
}

#[derive(Clone, Default, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct CameraWidget {
    pub camera_index: usize,
}

impl Widget for CameraWidget {
    fn title(&self) -> String {
        format!("Camera {}", self.camera_index + 1)
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        let world = context.world;
        let rect = ui.available_rect_before_wrap();
        let tile_id = context.current_tile_id;

        let mut cameras: Vec<Entity> = (unsafe { &*world }).query_entities(CAMERA).collect();
        cameras.sort_by_key(|entity| entity.id);
        let camera_entity = cameras.get(self.camera_index).copied();

        let texture_index = camera_entity.and_then(|cam| {
            (unsafe { &*world })
                .resources
                .user_interface
                .required_cameras
                .iter()
                .position(|&entity| entity == cam)
        });

        let clicked = if let Some(index) = texture_index
            && let Some(texture_id) = context.viewport_textures.get(index)
        {
            let image = egui::Image::new(egui::load::SizedTexture::new(
                *texture_id,
                egui::vec2(rect.width(), rect.height()),
            ))
            .fit_to_exact_size(egui::vec2(rect.width(), rect.height()))
            .sense(egui::Sense::click());

            let response = ui.put(rect, image);
            response.clicked()
        } else {
            let response = ui.allocate_rect(rect, egui::Sense::click());
            response.clicked()
        };

        let pixels_per_point = ui.ctx().pixels_per_point();

        if context.selected_viewport_tile.is_none() {
            *context.selected_viewport_tile = Some(tile_id);
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }
        }

        if clicked {
            *context.selected_viewport_tile = Some(tile_id);
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }
        }

        let is_selected = *context.selected_viewport_tile == Some(tile_id);
        if is_selected {
            (unsafe { &mut *world })
                .resources
                .window
                .active_viewport_rect = Some(nightshade::prelude::ViewportRect {
                x: rect.min.x * pixels_per_point,
                y: rect.min.y * pixels_per_point,
                width: rect.width() * pixels_per_point,
                height: rect.height() * pixels_per_point,
            });
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }

            ui.painter().rect_stroke(
                rect,
                egui::CornerRadius::ZERO,
                egui::Stroke::new(3.0, egui::Color32::from_rgb(255, 165, 0)),
                egui::StrokeKind::Inside,
            );
        }
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct MaterialsWidget {
    pub selected_material: Option<String>,
    #[serde(skip)]
    pub new_material_name: String,
    #[serde(default = "default_true")]
    pub materials_list_expanded: bool,
}

fn default_true() -> bool {
    true
}

impl Default for MaterialsWidget {
    fn default() -> Self {
        Self {
            selected_material: None,
            new_material_name: String::new(),
            materials_list_expanded: true,
        }
    }
}

impl Widget for MaterialsWidget {
    fn title(&self) -> String {
        "Materials".to_string()
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        let world = unsafe { &mut *context.world };
        let tile_id = context.current_tile_id;

        let available_rect = ui.available_rect_before_wrap();
        ui.painter()
            .rect_filled(available_rect, 0.0, ui.style().visuals.panel_fill);

        egui::ScrollArea::vertical()
            .id_salt(("materials_widget_scroll", tile_id))
            .auto_shrink([false, false])
            .show(ui, |ui| {
                ui.set_min_width(available_rect.width());
                ui.add_space(4.0);
                ui.horizontal(|ui| {
                    ui.label("New material:");
                    ui.text_edit_singleline(&mut self.new_material_name);
                    if ui.button("Create").clicked()
                        && !self.new_material_name.is_empty()
                        && !world
                            .resources
                            .material_registry
                            .registry
                            .name_to_index
                            .contains_key(&self.new_material_name)
                    {
                        material_registry_insert(
                            &mut world.resources.material_registry,
                            self.new_material_name.clone(),
                            Material::default(),
                        );
                        self.selected_material = Some(self.new_material_name.clone());
                        self.new_material_name.clear();
                    }
                });

                ui.separator();

                let material_names: Vec<String> = world
                    .resources
                    .material_registry
                    .registry
                    .index_to_name
                    .iter()
                    .filter_map(|opt| opt.clone())
                    .collect();

                let header_response =
                    ui.collapsing(format!("Materials ({})", material_names.len()), |ui| {
                        egui::ScrollArea::vertical()
                            .id_salt(tile_id)
                            .max_height(200.0)
                            .show(ui, |ui| {
                                for name in &material_names {
                                    let ref_count = world
                                        .resources
                                        .material_registry
                                        .registry
                                        .name_to_index
                                        .get(name)
                                        .map(|&index| {
                                            world
                                                .resources
                                                .material_registry
                                                .registry
                                                .reference_counts
                                                [index as usize]
                                        })
                                        .unwrap_or(0);
                                    let is_selected = self.selected_material.as_ref() == Some(name);

                                    ui.horizontal(|ui| {
                                        let label = format!("{} ({})", name, ref_count);
                                        if ui.selectable_label(is_selected, &label).clicked() {
                                            self.selected_material = Some(name.clone());
                                        }

                                        if ref_count == 0
                                            && ui
                                                .small_button("🗑")
                                                .on_hover_text("Delete")
                                                .clicked()
                                        {
                                            if let Some((index, generation)) = world
                                                .resources
                                                .material_registry
                                                .registry
                                                .lookup_index(name)
                                            {
                                                world
                                                    .resources
                                                    .material_registry
                                                    .registry
                                                    .remove(index, generation);
                                            }
                                            if self.selected_material.as_ref() == Some(name) {
                                                self.selected_material = None;
                                            }
                                        }
                                    });
                                }
                            });
                    });

                self.materials_list_expanded = header_response.fully_open();

                ui.separator();

                if let Some(material_name) = &self.selected_material.clone() {
                    if let Some(material) = registry_entry_by_name_mut(
                        &mut world.resources.material_registry.registry,
                        material_name,
                    ) {
                        ui.collapsing(format!("Edit: {}", material_name), |ui| {
                            ui.horizontal(|ui| {
                                ui.label("Base Color:");
                                ui.color_edit_button_rgba_unmultiplied(&mut material.base_color);
                            });

                            ui.horizontal(|ui| {
                                ui.label("R:");
                                ui.add(
                                    egui::Slider::new(&mut material.base_color[0], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("G:");
                                ui.add(
                                    egui::Slider::new(&mut material.base_color[1], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("B:");
                                ui.add(
                                    egui::Slider::new(&mut material.base_color[2], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("A:");
                                ui.add(
                                    egui::Slider::new(&mut material.base_color[3], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.separator();

                            ui.horizontal(|ui| {
                                ui.label("Alpha Mode:");
                                egui::ComboBox::from_id_salt(("alpha_mode", tile_id))
                                    .selected_text(match material.alpha_mode {
                                        AlphaMode::Opaque => "Opaque",
                                        AlphaMode::Mask => "Mask",
                                        AlphaMode::Blend => "Blend",
                                    })
                                    .show_ui(ui, |ui| {
                                        ui.selectable_value(
                                            &mut material.alpha_mode,
                                            AlphaMode::Opaque,
                                            "Opaque",
                                        );
                                        ui.selectable_value(
                                            &mut material.alpha_mode,
                                            AlphaMode::Mask,
                                            "Mask",
                                        );
                                        ui.selectable_value(
                                            &mut material.alpha_mode,
                                            AlphaMode::Blend,
                                            "Blend",
                                        );
                                    });
                            });

                            if material.alpha_mode == AlphaMode::Mask {
                                ui.horizontal(|ui| {
                                    ui.label("Alpha Cutoff:");
                                    ui.add(
                                        egui::Slider::new(&mut material.alpha_cutoff, 0.0..=1.0)
                                            .fixed_decimals(2),
                                    );
                                });
                            }

                            ui.separator();

                            ui.horizontal(|ui| {
                                ui.label("Roughness:");
                                ui.add(
                                    egui::Slider::new(&mut material.roughness, 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("Metallic:");
                                ui.add(
                                    egui::Slider::new(&mut material.metallic, 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("Unlit:");
                                ui.checkbox(&mut material.unlit, "");
                            });

                            ui.separator();

                            ui.label("Emissive Factor:");

                            ui.horizontal(|ui| {
                                ui.label("R:");
                                ui.add(
                                    egui::Slider::new(&mut material.emissive_factor[0], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("G:");
                                ui.add(
                                    egui::Slider::new(&mut material.emissive_factor[1], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.horizontal(|ui| {
                                ui.label("B:");
                                ui.add(
                                    egui::Slider::new(&mut material.emissive_factor[2], 0.0..=1.0)
                                        .fixed_decimals(2),
                                );
                            });

                            ui.separator();

                            ui.label("Textures:");

                            let mut base_texture_str =
                                material.base_texture.clone().unwrap_or_default();
                            ui.horizontal(|ui| {
                                ui.label("Base Texture:");
                                if ui.text_edit_singleline(&mut base_texture_str).changed() {
                                    material.base_texture = if base_texture_str.is_empty() {
                                        None
                                    } else {
                                        Some(base_texture_str)
                                    };
                                }
                            });

                            let mut emissive_texture_str =
                                material.emissive_texture.clone().unwrap_or_default();
                            ui.horizontal(|ui| {
                                ui.label("Emissive Texture:");
                                if ui.text_edit_singleline(&mut emissive_texture_str).changed() {
                                    material.emissive_texture = if emissive_texture_str.is_empty() {
                                        None
                                    } else {
                                        Some(emissive_texture_str)
                                    };
                                }
                            });
                        });
                    } else {
                        self.selected_material = None;
                    }
                }
            });
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
#[cfg_attr(target_arch = "wasm32", derive(Default))]
pub struct WebWidget {
    #[cfg(not(target_arch = "wasm32"))]
    pub url: String,
    #[cfg(not(target_arch = "wasm32"))]
    pub id: String,
}

#[cfg(not(target_arch = "wasm32"))]
impl Default for WebWidget {
    fn default() -> Self {
        Self {
            url: "https://matthewberger.dev/nightshade".to_string(),
            id: nightshade::prelude::uuid::Uuid::new_v4().to_string(),
        }
    }
}

impl Widget for WebWidget {
    fn title(&self) -> String {
        "Web".to_string()
    }

    fn ui(&mut self, ui: &mut egui::Ui, _context: &mut WidgetContext) {
        let rect = ui.available_rect_before_wrap();

        ui.painter()
            .rect_filled(rect, 0.0, ui.style().visuals.panel_fill);

        #[cfg(target_arch = "wasm32")]
        {
            ui.centered_and_justified(|ui| {
                ui.label("Web widget is unavailable on wasm");
            });
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            _context
                .web_widget_rects
                .push((self.id.clone(), self.url.clone(), rect));
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub enum AssetBrowserViewMode {
    #[default]
    Grid,
    List,
}

#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub enum AssetFilter {
    All,
    Meshes,
    Textures,
    Materials,
    Prefabs,
    Scripts,
    Audio,
}

impl Default for AssetFilter {
    fn default() -> Self {
        Self::All
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct AssetBrowserWidget {
    #[serde(skip)]
    pub current_path: std::path::PathBuf,
    #[serde(skip)]
    pub search_query: String,
    pub view_mode: AssetBrowserViewMode,
    pub filter: AssetFilter,
    #[serde(skip)]
    pub selected_asset: Option<std::path::PathBuf>,
}

impl Default for AssetBrowserWidget {
    fn default() -> Self {
        Self {
            current_path: std::path::PathBuf::new(),
            search_query: String::new(),
            view_mode: AssetBrowserViewMode::Grid,
            filter: AssetFilter::All,
            selected_asset: None,
        }
    }
}

impl Widget for AssetBrowserWidget {
    fn title(&self) -> String {
        "Asset Browser".to_string()
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        let tile_id = context.current_tile_id;
        let available_rect = ui.available_rect_before_wrap();

        ui.painter()
            .rect_filled(available_rect, 0.0, ui.style().visuals.panel_fill);

        egui::TopBottomPanel::top(egui::Id::new(("asset_browser_toolbar", tile_id))).show_inside(
            ui,
            |ui| {
                ui.horizontal(|ui| {
                    ui.label("Search:");
                    ui.text_edit_singleline(&mut self.search_query);

                    ui.separator();

                    egui::ComboBox::from_id_salt(("asset_filter", tile_id))
                        .selected_text(match self.filter {
                            AssetFilter::All => "All",
                            AssetFilter::Meshes => "Meshes",
                            AssetFilter::Textures => "Textures",
                            AssetFilter::Materials => "Materials",
                            AssetFilter::Prefabs => "Prefabs",
                            AssetFilter::Scripts => "Scripts",
                            AssetFilter::Audio => "Audio",
                        })
                        .show_ui(ui, |ui| {
                            ui.selectable_value(&mut self.filter, AssetFilter::All, "All");
                            ui.selectable_value(&mut self.filter, AssetFilter::Meshes, "Meshes");
                            ui.selectable_value(
                                &mut self.filter,
                                AssetFilter::Textures,
                                "Textures",
                            );
                            ui.selectable_value(
                                &mut self.filter,
                                AssetFilter::Materials,
                                "Materials",
                            );
                            ui.selectable_value(&mut self.filter, AssetFilter::Prefabs, "Prefabs");
                            ui.selectable_value(&mut self.filter, AssetFilter::Scripts, "Scripts");
                            ui.selectable_value(&mut self.filter, AssetFilter::Audio, "Audio");
                        });

                    ui.separator();

                    if ui
                        .selectable_label(self.view_mode == AssetBrowserViewMode::Grid, "Grid")
                        .clicked()
                    {
                        self.view_mode = AssetBrowserViewMode::Grid;
                    }
                    if ui
                        .selectable_label(self.view_mode == AssetBrowserViewMode::List, "List")
                        .clicked()
                    {
                        self.view_mode = AssetBrowserViewMode::List;
                    }
                });
            },
        );

        let project_path = context.current_project_path;

        egui::CentralPanel::default()
            .frame(egui::Frame::NONE.fill(ui.style().visuals.panel_fill))
            .show_inside(ui, |ui| {
                egui::ScrollArea::vertical()
                    .id_salt(("asset_browser_scroll", tile_id))
                    .auto_shrink([false, false])
                    .show(ui, |ui| {
                        self.show_assets(ui, project_path, tile_id);
                    });
            });
    }
}

impl AssetBrowserWidget {
    fn show_assets(
        &mut self,
        ui: &mut egui::Ui,
        project_path: Option<&std::path::PathBuf>,
        tile_id: egui_tiles::TileId,
    ) {
        let project_path = match project_path {
            Some(path) => path.clone(),
            None => {
                ui.centered_and_justified(|ui| {
                    ui.label("No project loaded");
                });
                return;
            }
        };

        let assets_path = project_path.join("assets");
        if !assets_path.exists() {
            ui.centered_and_justified(|ui| {
                ui.label("No assets folder found");
            });
            return;
        }

        if self.current_path.as_os_str().is_empty() {
            self.current_path = assets_path.clone();
        }

        ui.horizontal(|ui| {
            if ui.button("<<").clicked()
                && self.current_path != assets_path
                && let Some(parent) = self.current_path.parent()
                && (parent.starts_with(&assets_path) || parent == assets_path)
            {
                self.current_path = parent.to_path_buf();
            }
            ui.label(
                self.current_path
                    .strip_prefix(&project_path)
                    .unwrap_or(&self.current_path)
                    .display()
                    .to_string(),
            );
        });

        ui.separator();

        let entries = match std::fs::read_dir(&self.current_path) {
            Ok(entries) => entries.filter_map(|e| e.ok()).collect::<Vec<_>>(),
            Err(_) => {
                ui.label("Failed to read directory");
                return;
            }
        };

        let search_lower = self.search_query.to_lowercase();
        let filtered_entries: Vec<_> = entries
            .into_iter()
            .filter(|entry| {
                let name = entry.file_name().to_string_lossy().to_lowercase();
                if !self.search_query.is_empty() && !name.contains(&search_lower) {
                    return false;
                }
                if entry.path().is_dir() {
                    return true;
                }
                match self.filter {
                    AssetFilter::All => true,
                    AssetFilter::Meshes => {
                        name.ends_with(".gltf")
                            || name.ends_with(".glb")
                            || name.ends_with(".fbx")
                            || name.ends_with(".obj")
                    }
                    AssetFilter::Textures => {
                        name.ends_with(".png")
                            || name.ends_with(".jpg")
                            || name.ends_with(".jpeg")
                            || name.ends_with(".hdr")
                    }
                    AssetFilter::Materials => name.ends_with(".mat") || name.ends_with(".material"),
                    AssetFilter::Prefabs => name.ends_with(".prefab") || name.ends_with(".json"),
                    AssetFilter::Scripts => name.ends_with(".rhai") || name.ends_with(".lua"),
                    AssetFilter::Audio => {
                        name.ends_with(".wav") || name.ends_with(".mp3") || name.ends_with(".ogg")
                    }
                }
            })
            .collect();

        match self.view_mode {
            AssetBrowserViewMode::Grid => self.show_grid_view(ui, &filtered_entries, tile_id),
            AssetBrowserViewMode::List => self.show_list_view(ui, &filtered_entries),
        }
    }

    fn show_grid_view(
        &mut self,
        ui: &mut egui::Ui,
        entries: &[std::fs::DirEntry],
        tile_id: egui_tiles::TileId,
    ) {
        let item_size = 80.0;
        let spacing = 8.0;
        let available_width = ui.available_width();
        let items_per_row = ((available_width + spacing) / (item_size + spacing))
            .floor()
            .max(1.0) as usize;

        egui::Grid::new(("asset_browser_grid", tile_id))
            .num_columns(items_per_row)
            .spacing([spacing, spacing])
            .show(ui, |ui| {
                for (index, entry) in entries.iter().enumerate() {
                    let path = entry.path();
                    let is_dir = path.is_dir();
                    let name = entry.file_name().to_string_lossy().to_string();
                    let is_selected = self.selected_asset.as_ref() == Some(&path);

                    let icon = if is_dir {
                        "📁"
                    } else {
                        let lower_name = name.to_lowercase();
                        if lower_name.ends_with(".gltf")
                            || lower_name.ends_with(".glb")
                            || lower_name.ends_with(".fbx")
                        {
                            "🎮"
                        } else if lower_name.ends_with(".png")
                            || lower_name.ends_with(".jpg")
                            || lower_name.ends_with(".hdr")
                        {
                            "🖼"
                        } else if lower_name.ends_with(".rhai") || lower_name.ends_with(".lua") {
                            "📜"
                        } else if lower_name.ends_with(".wav") || lower_name.ends_with(".mp3") {
                            "🔊"
                        } else {
                            "📄"
                        }
                    };

                    let fill_color = if is_selected {
                        ui.visuals().selection.bg_fill
                    } else {
                        ui.visuals().extreme_bg_color
                    };

                    let is_draggable = !is_dir && Self::is_spawnable_asset(&path);
                    let item_id = egui::Id::new(("asset_item", tile_id, index));

                    let response = ui.scope(|ui| {
                        ui.set_min_width(item_size);
                        ui.set_max_width(item_size);

                        let frame = egui::Frame::NONE
                            .fill(fill_color)
                            .inner_margin(egui::Margin::same(4));

                        frame.show(ui, |ui| {
                            ui.centered_and_justified(|ui| {
                                ui.label(egui::RichText::new(icon).size(32.0));
                            });
                        });

                        let truncated_name = if name.len() > 12 {
                            format!("{}...", &name[..9])
                        } else {
                            name.clone()
                        };

                        ui.centered_and_justified(|ui| {
                            ui.label(egui::RichText::new(&truncated_name).small())
                                .on_hover_text(&name);
                        });
                    });

                    let item_rect = response.response.rect;
                    let item_response =
                        ui.interact(item_rect, item_id, egui::Sense::click_and_drag());

                    if is_draggable {
                        if item_response.drag_started() {
                            ui.ctx().data_mut(|data| {
                                data.insert_temp(egui::Id::new("dragged_asset_path"), path.clone());
                            });
                        }

                        if item_response.dragged() {
                            egui::Area::new(egui::Id::new("asset_drag_preview"))
                                .order(egui::Order::Tooltip)
                                .current_pos(ui.ctx().pointer_hover_pos().unwrap_or_default())
                                .show(ui.ctx(), |ui| {
                                    ui.label(format!("{} {}", icon, name));
                                });
                        }
                    }

                    if item_response.clicked() {
                        if is_dir {
                            self.current_path = path;
                            self.selected_asset = None;
                        } else {
                            self.selected_asset = Some(path);
                        }
                    }

                    if (index + 1) % items_per_row == 0 {
                        ui.end_row();
                    }
                }
            });
    }

    fn is_spawnable_asset(path: &std::path::Path) -> bool {
        if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
            matches!(ext.to_lowercase().as_str(), "gltf" | "glb" | "fbx" | "obj")
        } else {
            false
        }
    }

    fn show_list_view(&mut self, ui: &mut egui::Ui, entries: &[std::fs::DirEntry]) {
        for entry in entries {
            let path = entry.path();
            let is_dir = path.is_dir();
            let name = entry.file_name().to_string_lossy().to_string();
            let is_selected = self.selected_asset.as_ref() == Some(&path);

            let icon = if is_dir { "📁" } else { "📄" };

            let response = ui.selectable_label(is_selected, format!("{} {}", icon, name));

            if response.clicked() {
                if is_dir {
                    self.current_path = path;
                    self.selected_asset = None;
                } else {
                    self.selected_asset = Some(path);
                }
            }
        }
    }
}

#[derive(Clone, Default, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct SceneBrowserWidget {
    #[serde(skip)]
    pub pending_delete: Option<String>,
}

impl Widget for SceneBrowserWidget {
    fn title(&self) -> String {
        "Scene Browser".to_string()
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        let available_rect = ui.available_rect_before_wrap();
        let tile_id = context.current_tile_id;

        ui.painter()
            .rect_filled(available_rect, 0.0, ui.style().visuals.panel_fill);

        if let Some(scene_name) = self.pending_delete.clone() {
            let escape_pressed = ui.ctx().input(|i| i.key_pressed(egui::Key::Escape));
            let enter_pressed = ui.ctx().input(|i| i.key_pressed(egui::Key::Enter));

            if escape_pressed {
                self.pending_delete = None;
            } else if enter_pressed {
                context.scene_actions.push(SceneAction::Delete(scene_name));
                self.pending_delete = None;
            } else {
                let screen_rect = ui
                    .ctx()
                    .input(|i| i.viewport().inner_rect)
                    .unwrap_or(available_rect);
                egui::Area::new(egui::Id::new(("delete_scene_modal_backdrop", tile_id)))
                    .fixed_pos(screen_rect.min)
                    .order(egui::Order::Foreground)
                    .show(ui.ctx(), |ui| {
                        let response =
                            ui.allocate_response(screen_rect.size(), egui::Sense::click());
                        ui.painter().rect_filled(
                            screen_rect,
                            0.0,
                            egui::Color32::from_black_alpha(128),
                        );
                        if response.clicked() {
                            self.pending_delete = None;
                        }
                    });

                egui::Window::new("Delete Scene")
                    .id(egui::Id::new(("delete_scene_dialog", tile_id)))
                    .title_bar(false)
                    .collapsible(false)
                    .resizable(false)
                    .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
                    .order(egui::Order::Foreground)
                    .show(ui.ctx(), |ui| {
                        ui.heading("Delete Scene");
                        ui.add_space(4.0);
                        ui.label(format!("Delete \"{}\"?", scene_name));
                        ui.add_space(8.0);
                        ui.horizontal(|ui| {
                            if ui.button("Cancel").clicked() {
                                self.pending_delete = None;
                            }
                            if ui.button("Delete").clicked() {
                                context
                                    .scene_actions
                                    .push(SceneAction::Delete(scene_name.clone()));
                                self.pending_delete = None;
                            }
                        });
                    });
            }
        }

        egui::ScrollArea::vertical()
            .id_salt(("scene_browser_scroll", tile_id))
            .auto_shrink([false, false])
            .show(ui, |ui| {
                ui.set_min_width(available_rect.width());
                ui.add_space(4.0);

                ui.horizontal(|ui| {
                    ui.heading("Scenes");
                    ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                        #[cfg(not(target_arch = "wasm32"))]
                        if ui
                            .add_enabled(
                                context.scene_browser.has_project,
                                egui::Button::new("Import"),
                            )
                            .clicked()
                            && let Some(path) = rfd::FileDialog::new()
                                .add_filter("Scene files", &["json", "bin"])
                                .pick_file()
                        {
                            context.scene_actions.push(SceneAction::Import(path));
                        }
                    });
                });

                ui.separator();

                if !context.scene_browser.has_project {
                    ui.centered_and_justified(|ui| {
                        ui.label("No project loaded");
                    });
                } else if context.scene_browser.scenes.is_empty() {
                    ui.centered_and_justified(|ui| {
                        ui.label("No scenes in project");
                    });
                } else {
                    for scene_info in &context.scene_browser.scenes {
                        let frame_fill = if scene_info.is_active {
                            ui.visuals().selection.bg_fill.gamma_multiply(0.5)
                        } else {
                            egui::Color32::TRANSPARENT
                        };

                        egui::Frame::NONE
                            .fill(frame_fill)
                            .inner_margin(egui::Margin::same(8))
                            .show(ui, |ui| {
                                ui.horizontal(|ui| {
                                    let icon = if scene_info.is_active { "▶" } else { "○" };
                                    ui.label(egui::RichText::new(icon).size(14.0));

                                    ui.vertical(|ui| {
                                        let name_text =
                                            egui::RichText::new(&scene_info.name).strong();
                                        if ui.link(name_text).clicked() && !scene_info.is_active {
                                            context.scene_actions.push(SceneAction::SwitchTo(
                                                scene_info.name.clone(),
                                            ));
                                        }

                                        ui.label(
                                            egui::RichText::new(format!(
                                                "{} entities",
                                                scene_info.entity_count
                                            ))
                                            .small()
                                            .weak(),
                                        );
                                    });

                                    ui.with_layout(
                                        egui::Layout::right_to_left(egui::Align::Center),
                                        |ui| {
                                            let can_delete = context.scene_browser.scenes.len() > 1;
                                            if ui
                                                .add_enabled(
                                                    can_delete && !scene_info.is_active,
                                                    egui::Button::new("🗑").small(),
                                                )
                                                .on_hover_text("Delete scene")
                                                .clicked()
                                            {
                                                self.pending_delete = Some(scene_info.name.clone());
                                            }
                                        },
                                    );
                                });
                            });

                        ui.add_space(2.0);
                    }
                }
            });
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub enum EditorWidget {
    Camera(CameraWidget),
    Materials(MaterialsWidget),
    Web(WebWidget),
    AssetBrowser(AssetBrowserWidget),
    SceneBrowser(SceneBrowserWidget),
}

impl EditorWidget {
    pub fn from_kind(kind: WidgetKind) -> Self {
        match kind {
            WidgetKind::Camera => EditorWidget::Camera(CameraWidget::default()),
            WidgetKind::Materials => EditorWidget::Materials(MaterialsWidget::default()),
            WidgetKind::Web => EditorWidget::Web(WebWidget::default()),
            WidgetKind::AssetBrowser => EditorWidget::AssetBrowser(AssetBrowserWidget::default()),
            WidgetKind::SceneBrowser => EditorWidget::SceneBrowser(SceneBrowserWidget::default()),
        }
    }

    pub fn required_camera(&self, world: &World) -> Option<Entity> {
        match self {
            EditorWidget::Camera(widget) => {
                let mut cameras: Vec<Entity> = world.query_entities(CAMERA).collect();
                cameras.sort_by_key(|entity| entity.id);
                cameras.get(widget.camera_index).copied()
            }
            EditorWidget::Materials(_) => None,
            EditorWidget::Web(_) => None,
            EditorWidget::AssetBrowser(_) => None,
            EditorWidget::SceneBrowser(_) => None,
        }
    }
}

impl Widget for EditorWidget {
    fn title(&self) -> String {
        match self {
            EditorWidget::Camera(widget) => widget.title(),
            EditorWidget::Materials(widget) => widget.title(),
            EditorWidget::Web(widget) => widget.title(),
            EditorWidget::AssetBrowser(widget) => widget.title(),
            EditorWidget::SceneBrowser(widget) => widget.title(),
        }
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        match self {
            EditorWidget::Camera(widget) => widget.ui(ui, context),
            EditorWidget::Materials(widget) => widget.ui(ui, context),
            EditorWidget::Web(widget) => widget.ui(ui, context),
            EditorWidget::AssetBrowser(widget) => widget.ui(ui, context),
            EditorWidget::SceneBrowser(widget) => widget.ui(ui, context),
        }
    }
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct Pane {
    pub widget: EditorWidget,
}

impl Pane {
    pub fn new(kind: WidgetKind) -> Self {
        Self {
            widget: EditorWidget::from_kind(kind),
        }
    }

    pub fn camera(camera_index: usize) -> Self {
        Self {
            widget: EditorWidget::Camera(CameraWidget { camera_index }),
        }
    }
}
