use std::collections::HashMap;

use tray_icon::{
    menu::{IsMenuItem, Menu, MenuId, MenuItemKind, PredefinedMenuItem},
    TrayIconBuilder,
};

// The app winit thing can listen in on the same events
// coming from the state machine to give feedback to the user
// changing the icon, the alt texts, and the context menu
// based on the last received event

type Callback = Box<dyn Fn(&MenuItemKind) -> Result<(), anyhow::Error> + 'static>;

pub struct TrayIcon {
    icon: tray_icon::Icon,
    title: String,
    menu_items: HashMap<MenuId, Callback>,
    menu: Menu,
    tray_icon: Option<tray_icon::TrayIcon>,
}

#[allow(unused)]
impl TrayIcon {
    pub fn new(icon: tray_icon::Icon, title: &str) -> Self {
        Self {
            icon,
            title: title.to_string(),
            menu_items: HashMap::new(),
            menu: Menu::new(),
            tray_icon: None,
        }
    }

    pub fn add_seperator(&mut self) -> Result<(), anyhow::Error> {
        self.menu.append(&PredefinedMenuItem::separator())?;
        Ok(())
    }

    pub fn add_menu_item<F>(
        &mut self,
        item: &dyn IsMenuItem,
        callback: F,
    ) -> Result<MenuId, anyhow::Error>
    where
        F: Fn(&MenuItemKind) -> Result<(), anyhow::Error> + 'static,
    {
        let id = item.id().clone();
        self.menu.append(item)?;

        self.menu_items
            .insert(id.clone(), Box::new(callback) as Callback);

        Ok(id)
    }

    pub fn set_enabled(&self, id: &MenuId, enabled: bool) -> Result<(), anyhow::Error> {
        if let Some(item) = self.menu.items().iter().find(|i| i.id() == id) {
            if let Some(menuitem) = item.as_menuitem() {
                menuitem.set_enabled(enabled);
            }
            if let Some(menuitem) = item.as_check_menuitem() {
                menuitem.set_enabled(enabled);
            }
            if let Some(menuitem) = item.as_icon_menuitem() {
                menuitem.set_enabled(enabled);
            }
            if let Some(menuitem) = item.as_submenu() {
                menuitem.set_enabled(enabled);
            }
        }
        Ok(())
    }

    pub fn set_text(&self, id: &MenuId, text: &str) -> Result<(), anyhow::Error> {
        if let Some(item) = self.menu.items().iter().find(|i| i.id() == id) {
            if let Some(menuitem) = item.as_menuitem() {
                menuitem.set_text(text);
            }
            if let Some(menuitem) = item.as_check_menuitem() {
                menuitem.set_text(text);
            }
            if let Some(menuitem) = item.as_icon_menuitem() {
                menuitem.set_text(text);
            }
            if let Some(menuitem) = item.as_predefined_menuitem() {
                menuitem.set_text(text);
            }
            if let Some(menuitem) = item.as_submenu() {
                menuitem.set_text(text);
            }
        }
        Ok(())
    }

    pub fn handle(&self, id: &MenuId) {
        if let Some(item) = self.menu_items.get(id) {
            if let Some(i) = self.menu.items().iter().find(|i| i.id() == id) {
                if let Err(e) = item(i) {
                    tracing::error!("{:#?}", e);
                }
            }
        }
    }

    pub fn build(&mut self) -> Result<(), anyhow::Error> {
        self.tray_icon = Some(
            TrayIconBuilder::new()
                .with_tooltip(self.title.clone())
                .with_menu(Box::new(self.menu.clone()))
                .with_icon(self.icon.clone())
                .build()?,
        );

        Ok(())
    }
}
