use crate::mosaic::WidgetContext;
use nightshade::prelude::serde::{Deserialize, Serialize};
use nightshade::prelude::*;

use crate::app_context::AppContext;
use crate::messages::EditorMessage;

#[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, Copy, PartialEq, Eq)]
pub(crate) enum AssetCategory {
    Mesh,
    Texture,
    Material,
    Prefab,
    Script,
    Audio,
    Project,
    Other,
}

pub(crate) fn categorize_asset(name: &str) -> AssetCategory {
    let lower = name.to_lowercase();
    if lower.ends_with(".project.json") || lower.ends_with(".project.bin") {
        return AssetCategory::Project;
    }
    if lower.ends_with(".gltf")
        || lower.ends_with(".glb")
        || lower.ends_with(".fbx")
        || lower.ends_with(".obj")
    {
        AssetCategory::Mesh
    } else if lower.ends_with(".png")
        || lower.ends_with(".jpg")
        || lower.ends_with(".jpeg")
        || lower.ends_with(".hdr")
    {
        AssetCategory::Texture
    } else if lower.ends_with(".mat") || lower.ends_with(".material") {
        AssetCategory::Material
    } else if lower.ends_with(".prefab") || lower.ends_with(".json") || lower.ends_with(".bin") {
        AssetCategory::Prefab
    } else if lower.ends_with(".rhai") || lower.ends_with(".lua") {
        AssetCategory::Script
    } else if lower.ends_with(".wav") || lower.ends_with(".mp3") || lower.ends_with(".ogg") {
        AssetCategory::Audio
    } else {
        AssetCategory::Other
    }
}

fn asset_icon(category: AssetCategory) -> &'static str {
    match category {
        AssetCategory::Mesh => "\u{1f3ae}",
        AssetCategory::Texture => "\u{1f5bc}",
        AssetCategory::Script => "\u{1f4dc}",
        AssetCategory::Audio => "\u{1f50a}",
        AssetCategory::Material
        | AssetCategory::Prefab
        | AssetCategory::Project
        | AssetCategory::Other => "\u{1f4c4}",
    }
}

fn matches_filter(category: AssetCategory, filter: AssetFilter) -> bool {
    match filter {
        AssetFilter::All => true,
        AssetFilter::Meshes => category == AssetCategory::Mesh,
        AssetFilter::Textures => category == AssetCategory::Texture,
        AssetFilter::Materials => category == AssetCategory::Material,
        AssetFilter::Prefabs => category == AssetCategory::Prefab,
        AssetFilter::Scripts => category == AssetCategory::Script,
        AssetFilter::Audio => category == AssetCategory::Audio,
    }
}

#[derive(Clone)]
struct CachedEntry {
    path: std::path::PathBuf,
    file_name: String,
    file_name_lower: String,
    is_dir: bool,
}

#[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>,
    #[serde(skip)]
    cached_entries: Vec<CachedEntry>,
    #[serde(skip)]
    cache_path: std::path::PathBuf,
    #[serde(skip)]
    filtered_indices: Vec<usize>,
}

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,
            cached_entries: Vec::new(),
            cache_path: std::path::PathBuf::new(),
            filtered_indices: Vec::new(),
        }
    }
}

impl AssetBrowserWidget {
    pub(crate) fn render(
        &mut self,
        ui: &mut egui::Ui,
        context: &mut WidgetContext<AppContext, EditorMessage>,
    ) {
        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.app.project_edit.current_path.as_ref();

        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);
                    });
            });
    }

    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();

        if self.cache_path != self.current_path {
            match std::fs::read_dir(&self.current_path) {
                Ok(entries) => {
                    self.cached_entries = entries
                        .filter_map(|entry| entry.ok())
                        .map(|entry| {
                            let path = entry.path();
                            let is_dir = path.is_dir();
                            let file_name = entry.file_name().to_string_lossy().to_string();
                            let file_name_lower = file_name.to_lowercase();
                            CachedEntry {
                                path,
                                file_name,
                                file_name_lower,
                                is_dir,
                            }
                        })
                        .collect();
                    self.cache_path = self.current_path.clone();
                }
                Err(_) => {
                    ui.label("Failed to read directory");
                    return;
                }
            }
        }

        let search_lower = self.search_query.to_lowercase();
        self.filtered_indices.clear();
        self.filtered_indices.extend(
            self.cached_entries
                .iter()
                .enumerate()
                .filter(|(_, entry)| {
                    if !search_lower.is_empty() && !entry.file_name_lower.contains(&search_lower) {
                        return false;
                    }
                    if entry.is_dir {
                        return true;
                    }
                    matches_filter(categorize_asset(&entry.file_name), self.filter)
                })
                .map(|(index, _)| index),
        );

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

    fn show_grid_view(&mut self, ui: &mut egui::Ui, 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_index) in self.filtered_indices.iter().enumerate() {
                    let entry = &self.cached_entries[entry_index];
                    let path = &entry.path;
                    let is_dir = entry.is_dir;
                    let name = &entry.file_name;
                    let is_selected = self.selected_asset.as_ref() == Some(path);

                    let icon = if is_dir {
                        "\u{1f4c1}"
                    } else {
                        asset_icon(categorize_asset(name))
                    };

                    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.chars().count() > 12 {
                            let end = name
                                .char_indices()
                                .nth(9)
                                .map(|(index, _)| index)
                                .unwrap_or(name.len());
                            format!("{}...", &name[..end])
                        } else {
                            name.clone()
                        };

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

                    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.clone();
                            self.selected_asset = None;
                        } else {
                            self.selected_asset = Some(path.clone());
                        }
                    }

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

    fn is_spawnable_asset(path: &std::path::Path) -> bool {
        let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
        categorize_asset(name) == AssetCategory::Mesh
    }

    fn show_list_view(&mut self, ui: &mut egui::Ui) {
        for (index, &entry_index) in self.filtered_indices.iter().enumerate() {
            let entry = &self.cached_entries[entry_index];
            let is_selected = self.selected_asset.as_ref() == Some(&entry.path);

            let icon = if entry.is_dir {
                "\u{1f4c1}"
            } else {
                asset_icon(categorize_asset(&entry.file_name))
            };

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

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

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

            if is_draggable {
                if item_response.drag_started() {
                    ui.ctx().data_mut(|data| {
                        data.insert_temp(egui::Id::new("dragged_asset_path"), entry.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, entry.file_name));
                        });
                }
            }

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