use eframe::egui::{self, Color32, Stroke};

use crate::settings::GuiTheme;

macro_rules! color {
    ($value:expr) => {{
        let [red, green, blue, alpha] = $value.to_array();
        Color32::from_rgba_unmultiplied(red, green, blue, alpha)
    }};
}

pub(crate) fn apply(context: &egui::Context, selected: GuiTheme) {
    let theme = match selected {
        GuiTheme::Latte => catppuccin_egui::LATTE,
        GuiTheme::Frappe => catppuccin_egui::FRAPPE,
        GuiTheme::Macchiato => catppuccin_egui::MACCHIATO,
        GuiTheme::Mocha => catppuccin_egui::MOCHA,
    };
    let mut visuals = if selected.is_dark() {
        egui::Visuals::dark()
    } else {
        egui::Visuals::light()
    };

    visuals.hyperlink_color = color!(theme.rosewater);
    visuals.faint_bg_color = color!(theme.surface0);
    visuals.extreme_bg_color = color!(theme.crust);
    visuals.code_bg_color = color!(theme.mantle);
    visuals.warn_fg_color = color!(theme.peach);
    visuals.error_fg_color = color!(theme.maroon);
    visuals.window_fill = color!(theme.base);
    visuals.panel_fill = color!(theme.base);
    visuals.override_text_color = Some(color!(theme.text));
    visuals.weak_text_color = Some(color!(theme.subtext0));
    visuals.window_stroke.color = color!(theme.overlay1);

    let widget_stroke = color!(theme.overlay1);
    let widget_text = color!(theme.text);
    apply_widget(
        &mut visuals.widgets.noninteractive,
        color!(theme.base),
        widget_stroke,
        widget_text,
    );
    apply_widget(
        &mut visuals.widgets.inactive,
        color!(theme.surface0),
        widget_stroke,
        widget_text,
    );
    apply_widget(
        &mut visuals.widgets.hovered,
        color!(theme.surface2),
        widget_stroke,
        widget_text,
    );
    apply_widget(
        &mut visuals.widgets.active,
        color!(theme.surface1),
        widget_stroke,
        widget_text,
    );
    apply_widget(
        &mut visuals.widgets.open,
        color!(theme.surface0),
        widget_stroke,
        widget_text,
    );

    visuals.selection.bg_fill = color!(theme.blue).linear_multiply(0.2);
    visuals.selection.stroke.color = color!(theme.text);
    let shadow_color = Color32::from_black_alpha(96);
    visuals.window_shadow.color = shadow_color;
    visuals.popup_shadow.color = shadow_color;
    visuals.dark_mode = selected.is_dark();

    let egui_theme = if selected.is_dark() {
        egui::Theme::Dark
    } else {
        egui::Theme::Light
    };
    context.all_styles_mut(|style| style.visuals = visuals.clone());
    context.set_theme(egui_theme);
    context.request_repaint();
}

fn apply_widget(
    widget: &mut egui::style::WidgetVisuals,
    background: Color32,
    border: Color32,
    foreground: Color32,
) {
    widget.bg_fill = background;
    widget.weak_bg_fill = background;
    widget.bg_stroke = Stroke {
        color: border,
        ..widget.bg_stroke
    };
    widget.fg_stroke = Stroke {
        color: foreground,
        ..widget.fg_stroke
    };
}

#[cfg(test)]
mod tests {
    use super::apply;
    use crate::settings::GuiTheme;
    use eframe::egui;

    #[test]
    fn applies_macchiato() {
        let context = egui::Context::default();

        apply(&context, GuiTheme::Macchiato);
        assert_eq!(context.theme(), egui::Theme::Dark);
        assert_eq!(
            context.style_of(egui::Theme::Dark).visuals.panel_fill,
            egui::Color32::from_rgb(36, 39, 58)
        );
    }

    #[test]
    fn latte_uses_light_visuals() {
        let context = egui::Context::default();

        apply(&context, GuiTheme::Latte);
        assert_eq!(context.theme(), egui::Theme::Light);
        assert!(!context.style_of(egui::Theme::Light).visuals.dark_mode);
    }
}
