//! Exact Penpot status/action geometry for an expanded material-slot header.

use bevy_egui::egui;
use egui_phosphor_icons::icons;

use super::{MaterialSlotAction, MaterialSlotPanelViewModel};
use crate::ui::design_system::controls::{icon_button, icon_label, status};
use crate::ui::design_system::typography::TypeRole;
use crate::ui::design_system::{self, DesignPalette};

pub(super) const SLOT_STATUS_ACTION_WIDTH: f32 = 140.0;

#[derive(Debug, Clone, Copy, PartialEq)]
struct ExpandedHeaderActionGeometry {
    status: egui::Rect,
    menu: egui::Rect,
    browse: egui::Rect,
    locate: egui::Rect,
    clear: egui::Rect,
}

fn geometry(rect: egui::Rect) -> ExpandedHeaderActionGeometry {
    let status_row = egui::Rect::from_min_size(
        rect.min + egui::vec2(0.0, 5.333),
        egui::vec2(SLOT_STATUS_ACTION_WIDTH, 24.0),
    );
    let menu = egui::Rect::from_min_size(
        status_row.min + egui::vec2(116.0, 0.0),
        egui::vec2(24.0, 24.0),
    );
    let action_row = rect.min + egui::vec2(0.0, 34.667);
    ExpandedHeaderActionGeometry {
        status: egui::Rect::from_min_max(
            status_row.min,
            egui::pos2(menu.left() - 5.0, status_row.bottom()),
        ),
        menu,
        browse: egui::Rect::from_min_size(
            action_row + egui::vec2(56.0, 0.0),
            egui::vec2(24.0, 24.0),
        ),
        locate: egui::Rect::from_min_size(
            action_row + egui::vec2(86.0, 0.0),
            egui::vec2(24.0, 24.0),
        ),
        clear: egui::Rect::from_min_size(
            action_row + egui::vec2(116.0, 0.0),
            egui::vec2(24.0, 24.0),
        ),
    }
}

pub(super) fn expanded_status_actions(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    view: &MaterialSlotPanelViewModel,
    actions: &mut Vec<MaterialSlotAction>,
    diagnostics_ui: impl FnOnce(&mut egui::Ui),
    palette: DesignPalette,
) {
    let geometry = geometry(rect);
    rect_scope(ui, geometry.status, |ui| {
        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
            let (color, label, tooltip) = view.health.presentation(palette);
            status(ui, view.health.icon(), color, label, tooltip);
        });
    });
    rect_scope(ui, geometry.menu, |ui| {
        overflow_menu(ui, view, actions, diagnostics_ui);
    });

    for (button_rect, action, enabled, icon, tooltip) in [
        (
            geometry.browse,
            MaterialSlotAction::BrowseLibrary,
            true,
            icons::FOLDER_OPEN,
            "Browse project materials",
        ),
        (
            geometry.locate,
            MaterialSlotAction::Locate,
            view.can_locate,
            icons::CROSSHAIR,
            "Locate in Content Browser",
        ),
        (
            geometry.clear,
            MaterialSlotAction::Clear,
            view.can_clear,
            icons::X,
            "Clear actor assignment",
        ),
    ] {
        if !enabled {
            continue;
        }
        let clicked = rect_scope(ui, button_rect, |ui| {
            icon_button(ui, icon, tooltip, true).clicked()
        });
        if clicked {
            actions.push(action);
        }
    }
}

pub(super) fn overflow_menu(
    ui: &mut egui::Ui,
    view: &MaterialSlotPanelViewModel,
    actions: &mut Vec<MaterialSlotAction>,
    diagnostics_ui: impl FnOnce(&mut egui::Ui),
) {
    let palette = design_system::palette(ui);
    ui.menu_button(
        egui::RichText::new(icons::DOTS_THREE.as_str()).font(egui::FontId::new(
            16.0,
            egui::FontFamily::Name("phosphor-bold".into()),
        )),
        |ui| {
            if view.can_locate && menu_action(ui, icons::CROSSHAIR, "Locate", palette.text_primary)
            {
                actions.push(MaterialSlotAction::Locate);
                ui.close();
            }
            if menu_action(
                ui,
                icons::FOLDER_OPEN,
                "Browse Asset Library",
                palette.text_primary,
            ) {
                actions.push(MaterialSlotAction::BrowseLibrary);
                ui.close();
            }
            if view.can_clear && menu_action(ui, icons::X, "Clear material", palette.text_primary) {
                actions.push(MaterialSlotAction::Clear);
                ui.close();
            }
            if view.can_create_instance
                && menu_action(
                    ui,
                    icons::COPY,
                    "Create Instance and Assign",
                    palette.text_primary,
                )
            {
                actions.push(MaterialSlotAction::CreateInstance);
                ui.close();
            }
            if view.can_extract
                && menu_action(ui, icons::EXPORT, "Extract Editable…", palette.text_primary)
            {
                actions.push(MaterialSlotAction::ExtractEditable);
                ui.close();
            }
            ui.separator();
            ui.label(TypeRole::Section.text("Source & Diagnostics"));
            diagnostics_ui(ui);
        },
    )
    .response
    .on_hover_text("Material actions and diagnostics");
}

fn menu_action(
    ui: &mut egui::Ui,
    icon: egui_phosphor_icons::Icon,
    label: &str,
    color: egui::Color32,
) -> bool {
    ui.add(egui::Button::new(icon_label(
        icon,
        label,
        TypeRole::Control,
        14.0,
        color,
    )))
    .clicked()
}

fn rect_scope<R>(
    ui: &mut egui::Ui,
    rect: egui::Rect,
    add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
    ui.scope_builder(egui::UiBuilder::new().max_rect(rect), |ui| {
        ui.set_clip_rect(rect.intersect(ui.clip_rect()));
        add_contents(ui)
    })
    .inner
}

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

    #[test]
    fn expanded_status_and_actions_match_the_penpot_header_geometry() {
        let rect = egui::Rect::from_min_size(egui::pos2(448.0, 9.0), egui::vec2(140.0, 64.0));
        let geometry = geometry(rect);
        assert_eq!(geometry.menu.min, egui::pos2(564.0, 14.333));
        assert_eq!(geometry.browse.min, egui::pos2(504.0, 43.667));
        assert_eq!(geometry.locate.min, egui::pos2(534.0, 43.667));
        assert_eq!(geometry.clear.min, egui::pos2(564.0, 43.667));
        assert_eq!(geometry.menu.size(), egui::vec2(24.0, 24.0));
        assert_eq!(geometry.clear.size(), egui::vec2(24.0, 24.0));
        for child in [
            geometry.status,
            geometry.menu,
            geometry.browse,
            geometry.locate,
            geometry.clear,
        ] {
            assert!(rect.contains_rect(child));
        }
    }
}
