use super::*;

pub(super) fn content_empty_space(
    world: &mut World,
    response: egui::Response,
    current_folder: &str,
    visible_order: &[AssetSelection],
) {
    if response.clicked() || response.secondary_clicked() {
        world.resource_mut::<EditorAssets>().clear_selection();
    }
    response.context_menu(|ui| {
        empty_space_context_menu(world, ui, current_folder, visible_order);
    });
}

#[expect(
    clippy::too_many_arguments,
    reason = "asset grid rendering keeps immediate-mode UI inputs explicit"
)]
pub(super) fn asset_grid(
    world: &mut World,
    ui: &mut egui::Ui,
    selected_entities: &SelectedEntities,
    selected: &Option<AssetSelection>,
    folders: &[FolderSnapshot],
    assets: &[AssetRow],
    cache_snapshot: &ThumbnailCacheSnapshot,
    thumbnail_size: f32,
    visible_order: &[AssetSelection],
) {
    let thumb_size = thumbnail_size.clamp(48.0, 112.0);
    let cell_width = thumb_size + 20.0;
    let gap = 6.0;
    let columns = ((ui.available_width() + gap) / (cell_width + gap))
        .floor()
        .max(1.0) as usize;

    for folder_row in folders.chunks(columns) {
        ui.horizontal(|ui| {
            ui.spacing_mut().item_spacing = egui::vec2(gap, gap);
            for folder in folder_row {
                let selection = AssetSelection::Folder(folder.path.clone());
                let is_selected = world.resource::<EditorAssets>().is_selected(&selection);
                let response = draw_folder_cell(ui, folder, thumbnail_size, is_selected);
                handle_asset_drop_to_folder(world, ui, &response, &folder.path);
                if response.secondary_clicked() {
                    prepare_context_menu_selection(world, selection.clone());
                }
                if response.double_clicked() {
                    navigate_content_folder(world, folder.path.clone());
                } else if response.clicked() {
                    apply_content_click_selection(world, ui, selection.clone(), visible_order);
                }
                if response.drag_started() {
                    world
                        .resource_mut::<EditorAssets>()
                        .start_drag(selection.clone());
                }
                response.context_menu(|ui| folder_context_menu(world, ui, folder));
            }
        });
    }

    for asset_row in assets.chunks(columns) {
        ui.horizontal(|ui| {
            ui.spacing_mut().item_spacing = egui::vec2(gap, gap);
            for row in asset_row {
                draw_asset_grid_item(
                    world,
                    ui,
                    selected_entities,
                    selected,
                    row,
                    cache_snapshot,
                    thumbnail_size,
                    visible_order,
                );
            }
        });

        for row in asset_row {
            if row.asset.path.as_deref().is_some_and(|path| {
                world
                    .resource::<AssetBrowserUiState>()
                    .expanded_assets
                    .contains(path)
            }) {
                embedded_asset_shelf(
                    world,
                    ui,
                    selected_entities,
                    selected,
                    &row.asset,
                    cache_snapshot,
                    thumbnail_size,
                );
            }
        }
    }
}

pub(super) fn handle_asset_drop_to_folder(
    world: &mut World,
    ui: &egui::Ui,
    response: &egui::Response,
    destination_folder: &str,
) {
    if !response.hovered() || !ui.input(|input| input.pointer.any_released()) {
        return;
    }
    let dragging = world
        .resource::<EditorAssets>()
        .dragging_selection()
        .cloned();
    let Some(dragging) = dragging else {
        return;
    };
    let sources = if world.resource::<EditorAssets>().is_selected(&dragging) {
        selected_content_paths(world)
    } else {
        dragging
            .parent_path()
            .map(|path| vec![path.to_string()])
            .unwrap_or_default()
    };
    let operations: Vec<_> = sources
        .into_iter()
        .filter_map(|source| {
            let file_name = Path::new(&source).file_name()?;
            let destination = Path::new(destination_folder).join(file_name);
            (destination.as_path() != Path::new(&source)).then_some(
                content_pipeline::ContentOperation::Move {
                    source: PathBuf::from(source),
                    destination,
                },
            )
        })
        .collect();
    if !operations.is_empty() {
        queue_content_operations(world, operations, false, true);
    }
}

#[expect(
    clippy::too_many_arguments,
    reason = "asset rows keep selection, thumbnail, and visible-order UI inputs explicit"
)]
pub(super) fn draw_asset_grid_item(
    world: &mut World,
    ui: &mut egui::Ui,
    selected_entities: &SelectedEntities,
    _selected: &Option<AssetSelection>,
    row: &AssetRow,
    cache_snapshot: &ThumbnailCacheSnapshot,
    thumbnail_size: f32,
    visible_order: &[AssetSelection],
) {
    let selection = &row.selection;
    let asset = &row.asset;
    let is_selected = world.resource::<EditorAssets>().is_selected(selection);
    let failure = cache_snapshot.failure_reason(asset);
    let has_children = has_embedded_assets(world, asset);
    let document_status = asset.path.as_deref().and_then(|path| {
        world
            .get_resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
            .and_then(|store| store.snapshot_for_path(path))
    });
    let response = draw_asset_card(
        ui,
        asset,
        cache_snapshot.texture_for(asset),
        cache_snapshot.is_pending(asset),
        failure,
        is_selected,
        thumbnail_size,
        AssetCardStatus::for_document(
            document_status.as_ref(),
            document_status.as_ref().is_some_and(|status| status.dirty),
            failure.is_some(),
        ),
    );

    if has_children {
        let expanded = asset.path.as_deref().is_some_and(|path| {
            world
                .resource::<AssetBrowserUiState>()
                .expanded_assets
                .contains(path)
        });
        let icon = if expanded {
            icons::CARET_DOWN
        } else {
            icons::CARET_RIGHT
        };
        let button_rect = egui::Rect::from_min_size(
            response.rect.min + egui::vec2(6.0, 6.0),
            egui::vec2(18.0, 18.0),
        );
        let expand_response = ui.interact(
            button_rect,
            egui::Id::new((
                "asset_expand",
                asset.path.as_deref().unwrap_or(&asset.label),
            )),
            egui::Sense::click(),
        );
        ui.painter().rect(
            button_rect,
            3.0,
            if expand_response.hovered() {
                ELEVATED_BG
            } else {
                WIDGET_BG.linear_multiply(1.15)
            },
            egui::Stroke::new(1.0_f32, BORDER),
            egui::StrokeKind::Inside,
        );
        ui.painter().text(
            button_rect.center(),
            egui::Align2::CENTER_CENTER,
            icon.as_str(),
            egui::FontId::new(11.0, egui::FontFamily::Name(PHOSPHOR.into())),
            TEXT,
        );
        if expand_response.clicked() {
            if let Some(path) = asset.path.as_ref() {
                toggle_asset_expanded(world, path);
            }
        }
    }

    if response.double_clicked() {
        if failure.is_some() {
            let key = crate::assets::asset_cache_key(asset);
            crate::assets::retry_thumbnail(world, &key);
        } else {
            activate_content_asset(world, asset);
        }
    } else if response.clicked() {
        apply_content_click_selection(world, ui, selection.clone(), visible_order);
    }
    if response.drag_started() {
        world
            .resource_mut::<EditorAssets>()
            .start_drag(selection.clone());
    }
    if response.secondary_clicked() {
        prepare_context_menu_selection(world, selection.clone());
    }
    response.context_menu(|ui| {
        asset_context_menu(world, ui, asset, selected_entities);
    });
}

pub(super) fn embedded_asset_shelf(
    world: &mut World,
    ui: &mut egui::Ui,
    selected_entities: &SelectedEntities,
    selected: &Option<AssetSelection>,
    parent_asset: &EditorAsset,
    cache_snapshot: &ThumbnailCacheSnapshot,
    thumbnail_size: f32,
) {
    let embedded = embedded_assets_for_asset(world, parent_asset);
    if embedded.is_empty() {
        return;
    }
    prefetch_embedded_thumbnails(world, parent_asset, &embedded);
    let parent_texture = cache_snapshot.texture_for(parent_asset);

    ui.add_space(4.0);
    egui::Frame::new()
        .fill(WIDGET_BG.linear_multiply(0.72))
        .stroke(egui::Stroke::new(1.0_f32, BORDER.linear_multiply(0.85)))
        .corner_radius(egui::CornerRadius::same(4))
        .inner_margin(egui::Margin::symmetric(8, 5))
        .show(ui, |ui| {
            ui.set_min_width(ui.available_width());
            ui.horizontal(|ui| {
                ui.label(icon_text(icons::TREE_STRUCTURE, 13.0).color(TEXT_DIM));
                ui.small(
                    egui::RichText::new(format!("{} contents", parent_asset.label)).color(TEXT_DIM),
                );
            });
            egui::ScrollArea::horizontal()
                .id_salt(format!(
                    "embedded_shelf_{}",
                    parent_asset.path.as_deref().unwrap_or(&parent_asset.label)
                ))
                .max_height(thumbnail_size.clamp(48.0, 88.0) + 56.0)
                .auto_shrink([false, true])
                .show(ui, |ui| {
                    ui.horizontal(|ui| {
                        ui.spacing_mut().item_spacing = egui::vec2(7.0, 5.0);
                        for embedded_asset in &embedded {
                            draw_embedded_asset_cell(
                                world,
                                ui,
                                selected_entities,
                                selected,
                                embedded_asset,
                                cache_snapshot,
                                parent_texture,
                                thumbnail_size,
                            );
                        }
                    });
                });
        });
    ui.add_space(4.0);
}

pub(super) fn prefetch_embedded_thumbnails(
    world: &mut World,
    parent_asset: &EditorAsset,
    embedded: &[EmbeddedAsset],
) {
    let Some(parent_path) = parent_asset.path.clone() else {
        return;
    };
    let asset_server = world.resource::<AssetServer>().clone();
    let requests: Vec<EmbeddedAsset> = embedded.to_vec();
    world.resource_scope(|world, mut cache: Mut<AssetThumbnailCache>| {
        world.resource_scope(|_world, mut studio: Mut<crate::assets::ThumbnailStudio>| {
            for embedded_asset in &requests {
                match embedded_asset.kind {
                    AssetSubAssetKind::Mesh => {
                        let Some(mesh_label) = embedded_asset.mesh_label.clone() else {
                            continue;
                        };
                        cache.request_mesh_subasset(
                            embedded_asset.thumbnail_key.clone(),
                            parent_path.clone(),
                            mesh_label,
                            embedded_asset.material_label.clone(),
                            embedded_asset.requires_skinned_hierarchy,
                            &mut studio,
                        );
                    }
                    AssetSubAssetKind::Material => {
                        let Some(material_label) = embedded_asset.material_label.clone() else {
                            continue;
                        };
                        cache.request_source_material(
                            embedded_asset.thumbnail_key.clone(),
                            parent_path.clone(),
                            material_label,
                            &mut studio,
                        );
                    }
                    AssetSubAssetKind::Texture => {
                        let Some(texture_path) = embedded_asset.texture_path.clone() else {
                            continue;
                        };
                        cache.request_texture(
                            embedded_asset.thumbnail_key.clone(),
                            texture_path,
                            &asset_server,
                        );
                    }
                    AssetSubAssetKind::Skeleton | AssetSubAssetKind::AnimationClip => {}
                }
            }
        });
    });
}

#[expect(
    clippy::too_many_arguments,
    reason = "embedded asset rendering keeps immediate-mode UI inputs explicit"
)]
pub(super) fn draw_embedded_asset_cell(
    world: &mut World,
    ui: &mut egui::Ui,
    selected_entities: &SelectedEntities,
    selected: &Option<AssetSelection>,
    embedded: &EmbeddedAsset,
    cache_snapshot: &ThumbnailCacheSnapshot,
    _parent_texture: Option<egui::TextureId>,
    thumbnail_size: f32,
) {
    let selected = selected.as_ref() == Some(&embedded.selection);
    let texture_id = cache_snapshot
        .texture_for_key(&embedded.thumbnail_key)
        .or_else(|| {
            embedded
                .texture_path
                .as_deref()
                .and_then(|path| thumbnail_for_path(world, cache_snapshot, path))
        });
    let pending = cache_snapshot.is_pending_key(&embedded.thumbnail_key);
    let failure = cache_snapshot.failure_reason_for_key(&embedded.thumbnail_key);
    let failed = failure.is_some();
    let thumb_size = thumbnail_size.clamp(44.0, 64.0);
    let cell_size = egui::vec2(104.0, thumb_size + 50.0);
    let (rect, response) = ui.allocate_exact_size(cell_size, egui::Sense::click_and_drag());
    let response = if embedded.requires_skinned_hierarchy {
        response.on_hover_text(
            "Skinned primitives place through the dedicated renderer so their joint hierarchy remains intact.",
        )
    } else {
        response
    };
    let response = if let Some(reason) = failure {
        response.on_hover_text(reason)
    } else {
        response
    };
    let fill = if selected {
        crate::ui::theme::SELECTION_BG_MUTED
    } else if response.hovered() {
        WIDGET_BG.linear_multiply(1.2)
    } else {
        WIDGET_BG
    };
    ui.painter().rect(
        rect,
        4.0,
        fill,
        egui::Stroke::new(1.0_f32, if selected { ACCENT } else { BORDER }),
        egui::StrokeKind::Inside,
    );
    let thumb_rect = egui::Rect::from_min_size(
        egui::pos2(rect.center().x - thumb_size * 0.5, rect.min.y + 8.0),
        egui::vec2(thumb_size, thumb_size),
    );
    if let Some(texture_id) = texture_id {
        ui.painter().image(
            texture_id,
            thumb_rect,
            egui::Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)),
            egui::Color32::WHITE,
        );
    } else {
        ui.painter().rect(
            thumb_rect,
            4.0,
            ELEVATED_BG.linear_multiply(0.6),
            egui::Stroke::new(1.0_f32, BORDER),
            egui::StrokeKind::Inside,
        );
        let icon = if pending {
            icons::CIRCLE_NOTCH
        } else if failed {
            icons::WARNING_CIRCLE
        } else {
            subasset_icon(embedded.kind)
        };
        ui.painter().text(
            thumb_rect.center(),
            egui::Align2::CENTER_CENTER,
            icon.as_str(),
            egui::FontId::new(24.0, egui::FontFamily::Name(PHOSPHOR.into())),
            TEXT,
        );
    }

    let label = compact_subasset_label(embedded);
    let detail = compact_text(&embedded.detail, 18);
    let label_pos = egui::pos2(rect.center().x, thumb_rect.max.y + 7.0);
    ui.painter().text(
        label_pos,
        egui::Align2::CENTER_TOP,
        label.as_str(),
        egui::FontId::new(12.0, egui::FontFamily::Proportional),
        if selected {
            crate::ui::theme::TEXT_SELECTED
        } else {
            TEXT
        },
    );
    ui.painter().text(
        label_pos + egui::vec2(0.0, 17.0),
        egui::Align2::CENTER_TOP,
        format!("{} | {}", subasset_kind_label(embedded.kind), detail),
        egui::FontId::new(10.0, egui::FontFamily::Proportional),
        TEXT_DIM,
    );

    if response.clicked() {
        world
            .resource_mut::<EditorAssets>()
            .select(embedded.selection.clone());
    }
    if response.secondary_clicked() {
        prepare_context_menu_selection(world, embedded.selection.clone());
    }
    if response.drag_started() && embedded.kind != AssetSubAssetKind::Skeleton {
        world
            .resource_mut::<EditorAssets>()
            .start_drag(embedded.selection.clone());
    }
    response.context_menu(|ui| {
        subasset_context_menu(world, ui, embedded, selected_entities);
    });
}

pub(super) fn compact_subasset_label(embedded: &EmbeddedAsset) -> String {
    let base = match embedded.kind {
        AssetSubAssetKind::Mesh => embedded
            .label
            .split_once(" / ")
            .map(|(head, _)| head)
            .unwrap_or(&embedded.label),
        AssetSubAssetKind::Material
        | AssetSubAssetKind::Texture
        | AssetSubAssetKind::Skeleton
        | AssetSubAssetKind::AnimationClip => embedded.label.as_str(),
    };
    compact_text(base, 16)
}

pub(super) fn compact_text(text: &str, max_chars: usize) -> String {
    let mut chars = text.chars();
    let mut compact = String::new();
    for _ in 0..max_chars {
        let Some(ch) = chars.next() else {
            return text.to_string();
        };
        compact.push(ch);
    }
    if chars.next().is_some() {
        compact.push_str("...");
    }
    compact
}

pub(super) fn thumbnail_for_path(
    world: &World,
    cache_snapshot: &ThumbnailCacheSnapshot,
    path: &str,
) -> Option<egui::TextureId> {
    let assets = world.resource::<EditorAssets>();
    assets
        .assets
        .iter()
        .find(|asset| asset.path.as_deref() == Some(path))
        .and_then(|asset| cache_snapshot.texture_for(asset))
}

pub(super) fn toggle_asset_expanded(world: &mut World, path: &str) {
    let mut state = world.resource_mut::<AssetBrowserUiState>();
    if !state.expanded_assets.insert(path.to_string()) {
        state.expanded_assets.remove(path);
    }
}

pub(super) fn subasset_icon(kind: AssetSubAssetKind) -> Icon {
    match kind {
        AssetSubAssetKind::Mesh => icons::CUBE,
        AssetSubAssetKind::Material => icons::PALETTE,
        AssetSubAssetKind::Texture => icons::IMAGE,
        AssetSubAssetKind::Skeleton => icons::BONE,
        AssetSubAssetKind::AnimationClip => icons::FILM_STRIP,
    }
}

pub(super) fn subasset_kind_label(kind: AssetSubAssetKind) -> &'static str {
    match kind {
        AssetSubAssetKind::Mesh => "Mesh",
        AssetSubAssetKind::Material => "Material",
        AssetSubAssetKind::Texture => "Texture",
        AssetSubAssetKind::Skeleton => "Skeleton",
        AssetSubAssetKind::AnimationClip => "Animation Clip",
    }
}
