//! Penpot-styled material and texture asset pickers.

use bevy_egui::egui;
use egui_phosphor_icons::icons;
use shared::EditorAssetRef;

use super::panel::{MaterialPickerCandidate, MaterialSlotAction, MaterialSlotPanelViewModel};
use super::{MaterialTextureCandidate, MaterialThumbnailPresentation, TextureSlotResponse};
use crate::ui::design_system;
use crate::ui::design_system::controls::icon_label;
use crate::ui::design_system::typography::TypeRole;

const MATERIAL_PICKER_WIDTH: f32 = 244.0;
const TEXTURE_PICKER_WIDTH: f32 = 420.0;
const PICKER_ROW_HEIGHT: f32 = 44.0;
const PICKER_THUMBNAIL: f32 = 36.0;

pub(super) fn material_picker_popup(
    _ui: &mut egui::Ui,
    id: egui::Id,
    anchor: &egui::Response,
    view: &MaterialSlotPanelViewModel,
    actions: &mut Vec<MaterialSlotAction>,
) {
    egui::Popup::menu(anchor)
        .id(id)
        .width(MATERIAL_PICKER_WIDTH)
        .show(|ui| {
            ui.label(
                TypeRole::Section
                    .text("Material Assets")
                    .color(design_system::palette(ui).text_primary),
            );
            if let Some(current) = view.effective.as_ref() {
                if let Some(candidate) = find_material_candidate(&view.candidates, current) {
                    material_menu_row(ui, candidate, true, actions);
                } else {
                    unresolved_material_menu_row(ui, current);
                }
            }

            let recent_keys = recent_keys(ui, "material_picker_recents");
            let recent = candidates_for_recent_keys(&view.candidates, &recent_keys);
            for candidate in recent.into_iter().take(4) {
                let selected = view
                    .effective
                    .as_ref()
                    .is_some_and(|current| same_reference(current, &candidate.reference));
                if !selected {
                    material_menu_row(ui, candidate, false, actions);
                }
            }
            picker_footer(ui, view.assigned.is_some(), actions);
        });
}

pub(super) fn texture_picker_popup(
    _ui: &mut egui::Ui,
    id: egui::Id,
    anchor: &egui::Response,
    candidates: &[MaterialTextureCandidate],
    selected: Option<&MaterialTextureCandidate>,
    response: &mut TextureSlotResponse,
) {
    egui::Popup::menu(anchor)
        .id(id)
        .width(TEXTURE_PICKER_WIDTH)
        .show(|ui| {
            picker_heading(
                ui,
                "Select Texture",
                "Project textures and imported subassets",
            );
            if let Some(current) = selected {
                picker_section_label(ui, "CURRENT");
                texture_candidate_row(ui, current, true, response);
            }
            let recent_keys = recent_keys(ui, "texture_picker_recents");
            let recent = candidates_for_recent_texture_keys(candidates, &recent_keys);
            if !recent.is_empty() {
                picker_section_label(ui, "RECENT");
                for candidate in recent.into_iter().take(4) {
                    texture_candidate_row(ui, candidate, false, response);
                }
            }
            picker_section_label(ui, "PROJECT TEXTURES");
            egui::ScrollArea::vertical()
                .id_salt(id.with("project_textures"))
                .max_height(240.0)
                .auto_shrink([false, true])
                .show(ui, |ui| {
                    if candidates.is_empty() {
                        empty_picker_row(ui, "No project textures");
                    }
                    for candidate in candidates {
                        let selected = selected.is_some_and(|current| {
                            same_reference(&current.reference, &candidate.reference)
                        });
                        texture_candidate_row(ui, candidate, selected, response);
                    }
                });
            let palette = design_system::palette(ui);
            ui.separator();
            if ui
                .add(egui::Button::new(icon_label(
                    icons::FOLDER_OPEN,
                    "Browse Asset Library",
                    TypeRole::Control,
                    14.0,
                    palette.text_primary,
                )))
                .clicked()
            {
                response.browse_library = true;
                ui.close();
            }
            if selected.is_some()
                && ui
                    .add(egui::Button::new(icon_label(
                        icons::X,
                        "Clear Texture",
                        TypeRole::Control,
                        14.0,
                        palette.text_primary,
                    )))
                    .clicked()
            {
                response.clear = true;
                ui.close();
            }
        });
}

fn picker_heading(ui: &mut egui::Ui, title: &str, subtitle: &str) {
    let palette = design_system::palette(ui);
    ui.label(TypeRole::Title.text(title).color(palette.text_primary));
    ui.label(TypeRole::Small.text(subtitle).color(palette.text_muted));
    ui.separator();
}

fn picker_section_label(ui: &mut egui::Ui, label: &str) {
    let palette = design_system::palette(ui);
    ui.add_space(4.0);
    ui.label(TypeRole::Caption.text(label).color(palette.text_muted));
}

fn material_menu_row(
    ui: &mut egui::Ui,
    candidate: &MaterialPickerCandidate,
    selected: bool,
    actions: &mut Vec<MaterialSlotAction>,
) {
    let response = picker_asset_row(
        ui,
        &candidate.label,
        &candidate.detail,
        candidate.thumbnail,
        selected,
    );
    if response.clicked() {
        remember_recent(ui, "material_picker_recents", &candidate.reference);
        actions.push(MaterialSlotAction::Assign {
            reference: candidate.reference.clone(),
            from_drop: false,
        });
        ui.close();
    }
}

fn texture_candidate_row(
    ui: &mut egui::Ui,
    candidate: &MaterialTextureCandidate,
    selected: bool,
    response: &mut TextureSlotResponse,
) {
    let row = picker_asset_row(
        ui,
        &candidate.label,
        &candidate.detail,
        candidate.thumbnail,
        selected,
    );
    if row.clicked() {
        remember_recent(ui, "texture_picker_recents", &candidate.reference);
        response.selected = Some(candidate.clone());
        ui.close();
    }
}

fn picker_asset_row(
    ui: &mut egui::Ui,
    label: &str,
    detail: &str,
    thumbnail: MaterialThumbnailPresentation,
    selected: bool,
) -> egui::Response {
    let palette = design_system::palette(ui);
    let (rect, response) = ui.allocate_exact_size(
        egui::vec2(ui.available_width().max(1.0), PICKER_ROW_HEIGHT),
        egui::Sense::click(),
    );
    let fill = if selected {
        palette.accent_dark
    } else if response.hovered() {
        palette.elevated
    } else {
        palette.recessed
    };
    ui.painter().rect(
        rect,
        5.0,
        fill,
        egui::Stroke::new(
            1.0_f32,
            if selected {
                palette.accent
            } else {
                palette.border
            },
        ),
        egui::StrokeKind::Inside,
    );
    let thumb = egui::Rect::from_min_size(
        rect.left_top() + egui::vec2(4.0, 4.0),
        egui::vec2(PICKER_THUMBNAIL, PICKER_THUMBNAIL),
    );
    paint_thumbnail(ui, thumb, thumbnail);
    let text_rect = egui::Rect::from_min_max(
        egui::pos2(thumb.right() + 8.0, rect.top() + 5.0),
        egui::pos2(rect.right() - 8.0, rect.bottom() - 5.0),
    );
    let mut child = ui.new_child(
        egui::UiBuilder::new()
            .max_rect(text_rect)
            .layout(egui::Layout::top_down(egui::Align::Min)),
    );
    child.set_clip_rect(rect.intersect(ui.clip_rect()));
    child
        .add(egui::Label::new(TypeRole::Body.text(label).color(palette.text_primary)).truncate())
        .on_hover_text(label);
    child
        .add(egui::Label::new(TypeRole::Small.text(detail).color(palette.text_muted)).truncate())
        .on_hover_text(detail);
    response
}

fn paint_thumbnail(ui: &egui::Ui, rect: egui::Rect, thumbnail: MaterialThumbnailPresentation) {
    let palette = design_system::palette(ui);
    ui.painter().rect_filled(rect, 4.0, palette.control);
    match thumbnail {
        MaterialThumbnailPresentation::Ready(texture) => {
            ui.painter().image(
                texture,
                rect,
                egui::Rect::from_min_max(egui::Pos2::ZERO, egui::pos2(1.0, 1.0)),
                egui::Color32::WHITE,
            );
        }
        MaterialThumbnailPresentation::Pending => {
            ui.painter().text(
                rect.center(),
                egui::Align2::CENTER_CENTER,
                icons::CIRCLE_NOTCH.as_str(),
                egui::FontId::new(14.0, egui::FontFamily::Name("phosphor-bold".into())),
                palette.text_muted,
            );
        }
        MaterialThumbnailPresentation::Failed => {
            ui.painter().text(
                rect.center(),
                egui::Align2::CENTER_CENTER,
                icons::WARNING.as_str(),
                egui::FontId::new(14.0, egui::FontFamily::Name("phosphor-bold".into())),
                palette.error,
            );
        }
    }
    ui.painter().rect_stroke(
        rect,
        4.0,
        egui::Stroke::new(1.0_f32, palette.border),
        egui::StrokeKind::Inside,
    );
}

fn picker_footer(ui: &mut egui::Ui, can_clear: bool, actions: &mut Vec<MaterialSlotAction>) {
    let palette = design_system::palette(ui);
    ui.separator();
    if ui
        .add(egui::Button::new(icon_label(
            icons::FOLDER_OPEN,
            "Browse Asset Library",
            TypeRole::Control,
            14.0,
            palette.text_primary,
        )))
        .clicked()
    {
        actions.push(MaterialSlotAction::BrowseLibrary);
        ui.close();
    }
    if can_clear
        && ui
            .add(egui::Button::new(icon_label(
                icons::X,
                "Clear Material",
                TypeRole::Control,
                14.0,
                palette.text_primary,
            )))
            .clicked()
    {
        actions.push(MaterialSlotAction::Clear);
        ui.close();
    }
}

fn unresolved_material_menu_row(ui: &mut egui::Ui, current: &EditorAssetRef) {
    let palette = design_system::palette(ui);
    ui.add_sized(
        [ui.available_width().max(1.0), 28.0],
        egui::Button::new(TypeRole::Body.text(&current.label).color(palette.error)).selected(true),
    )
    .on_hover_text("The currently assigned material could not be resolved");
}

fn empty_picker_row(ui: &mut egui::Ui, message: &str) {
    let palette = design_system::palette(ui);
    ui.add_sized(
        [ui.available_width().max(1.0), 32.0],
        egui::Label::new(TypeRole::Body.text(message).color(palette.text_muted)),
    );
}

fn same_reference(left: &EditorAssetRef, right: &EditorAssetRef) -> bool {
    left.asset_id == right.asset_id && left.sub_asset_id == right.sub_asset_id
}

fn find_material_candidate<'a>(
    candidates: &'a [MaterialPickerCandidate],
    reference: &EditorAssetRef,
) -> Option<&'a MaterialPickerCandidate> {
    candidates
        .iter()
        .find(|candidate| same_reference(&candidate.reference, reference))
}

fn recent_keys(ui: &egui::Ui, salt: &'static str) -> Vec<(String, String)> {
    let id = ui.make_persistent_id(salt);
    ui.ctx()
        .data_mut(|data| data.get_persisted::<Vec<(String, String)>>(id))
        .unwrap_or_default()
}

fn remember_recent(ui: &egui::Ui, salt: &'static str, reference: &EditorAssetRef) {
    let id = ui.make_persistent_id(salt);
    let mut keys = recent_keys(ui, salt);
    let key = (reference.asset_id.clone(), reference.sub_asset_id.clone());
    keys.retain(|current| current != &key);
    keys.insert(0, key);
    keys.truncate(8);
    ui.ctx().data_mut(|data| data.insert_persisted(id, keys));
}

fn candidates_for_recent_keys<'a>(
    candidates: &'a [MaterialPickerCandidate],
    keys: &[(String, String)],
) -> Vec<&'a MaterialPickerCandidate> {
    keys.iter()
        .filter_map(|(asset_id, sub_asset_id)| {
            candidates.iter().find(|candidate| {
                candidate.reference.asset_id == *asset_id
                    && candidate.reference.sub_asset_id == *sub_asset_id
            })
        })
        .collect()
}

fn candidates_for_recent_texture_keys<'a>(
    candidates: &'a [MaterialTextureCandidate],
    keys: &[(String, String)],
) -> Vec<&'a MaterialTextureCandidate> {
    keys.iter()
        .filter_map(|(asset_id, sub_asset_id)| {
            candidates.iter().find(|candidate| {
                candidate.reference.asset_id == *asset_id
                    && candidate.reference.sub_asset_id == *sub_asset_id
            })
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn penpot_picker_reference_comparison_uses_stable_identity_not_label() {
        let left = EditorAssetRef::new("asset", "material:source", "Left");
        let right = EditorAssetRef::new("asset", "material:source", "Renamed");
        assert!(same_reference(&left, &right));
    }

    #[test]
    fn penpot_picker_geometry_matches_penpot_assets() {
        assert_eq!(MATERIAL_PICKER_WIDTH, 244.0);
        assert_eq!(TEXTURE_PICKER_WIDTH, 420.0);
        assert_eq!(PICKER_ROW_HEIGHT, 44.0);
        assert_eq!(PICKER_THUMBNAIL, 36.0);
    }
}
