use assistant::{AssistantPanel, InlineAssist};
use editor::Editor;

use gpui::{
    Action, ClickEvent, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Styled,
    Subscription, View, ViewContext, WeakView,
};
use search::{buffer_search, BufferSearchBar};
use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip};
use workspace::{
    item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
};

pub struct QuickActionBar {
    buffer_search_bar: View<BufferSearchBar>,
    active_item: Option<Box<dyn ItemHandle>>,
    _inlay_hints_enabled_subscription: Option<Subscription>,
    workspace: WeakView<Workspace>,
}

impl QuickActionBar {
    pub fn new(buffer_search_bar: View<BufferSearchBar>, workspace: &Workspace) -> Self {
        Self {
            buffer_search_bar,
            active_item: None,
            _inlay_hints_enabled_subscription: None,
            workspace: workspace.weak_handle(),
        }
    }

    fn active_editor(&self) -> Option<View<Editor>> {
        self.active_item
            .as_ref()
            .and_then(|item| item.downcast::<Editor>())
    }
}

impl Render for QuickActionBar {
    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
        let Some(editor) = self.active_editor() else {
            return div().id("empty quick action bar");
        };

        let inlay_hints_button = Some(QuickActionBarButton::new(
            "toggle inlay hints",
            Icon::InlayHint,
            editor.read(cx).inlay_hints_enabled(),
            Box::new(editor::ToggleInlayHints),
            "Toggle Inlay Hints",
            {
                let editor = editor.clone();
                move |_, cx| {
                    editor.update(cx, |editor, cx| {
                        editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
                    });
                }
            },
        ))
        .filter(|_| editor.read(cx).supports_inlay_hints(cx));

        let search_button = Some(QuickActionBarButton::new(
            "toggle buffer search",
            Icon::MagnifyingGlass,
            !self.buffer_search_bar.read(cx).is_dismissed(),
            Box::new(buffer_search::Deploy { focus: false }),
            "Buffer Search",
            {
                let buffer_search_bar = self.buffer_search_bar.clone();
                move |_, cx| {
                    buffer_search_bar.update(cx, |search_bar, cx| {
                        search_bar.toggle(&buffer_search::Deploy { focus: true }, cx)
                    });
                }
            },
        ))
        .filter(|_| editor.is_singleton(cx));

        let assistant_button = QuickActionBarButton::new(
            "toggle inline assistant",
            Icon::MagicWand,
            false,
            Box::new(InlineAssist),
            "Inline Assist",
            {
                let workspace = self.workspace.clone();
                move |_, cx| {
                    if let Some(workspace) = workspace.upgrade() {
                        workspace.update(cx, |workspace, cx| {
                            AssistantPanel::inline_assist(workspace, &InlineAssist, cx);
                        });
                    }
                }
            },
        );

        h_stack()
            .id("quick action bar")
            .p_1()
            .gap_2()
            .children(inlay_hints_button)
            .children(search_button)
            .child(assistant_button)
    }
}

impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}

#[derive(IntoElement)]
struct QuickActionBarButton {
    id: ElementId,
    icon: Icon,
    toggled: bool,
    action: Box<dyn Action>,
    tooltip: SharedString,
    on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>,
}

impl QuickActionBarButton {
    fn new(
        id: impl Into<ElementId>,
        icon: Icon,
        toggled: bool,
        action: Box<dyn Action>,
        tooltip: impl Into<SharedString>,
        on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
    ) -> Self {
        Self {
            id: id.into(),
            icon,
            toggled,
            action,
            tooltip: tooltip.into(),
            on_click: Box::new(on_click),
        }
    }
}

impl RenderOnce for QuickActionBarButton {
    fn render(self, _: &mut WindowContext) -> impl IntoElement {
        let tooltip = self.tooltip.clone();
        let action = self.action.boxed_clone();

        IconButton::new(self.id.clone(), self.icon)
            .size(ButtonSize::Compact)
            .icon_size(IconSize::Small)
            .style(ButtonStyle::Subtle)
            .selected(self.toggled)
            .tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
            .on_click(move |event, cx| (self.on_click)(event, cx))
    }
}

impl ToolbarItemView for QuickActionBar {
    fn set_active_pane_item(
        &mut self,
        active_pane_item: Option<&dyn ItemHandle>,
        cx: &mut ViewContext<Self>,
    ) -> ToolbarItemLocation {
        match active_pane_item {
            Some(active_item) => {
                self.active_item = Some(active_item.boxed_clone());
                self._inlay_hints_enabled_subscription.take();

                if let Some(editor) = active_item.downcast::<Editor>() {
                    let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
                    let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
                    self._inlay_hints_enabled_subscription =
                        Some(cx.observe(&editor, move |_, editor, cx| {
                            let editor = editor.read(cx);
                            let new_inlay_hints_enabled = editor.inlay_hints_enabled();
                            let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
                            let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
                                || supports_inlay_hints != new_supports_inlay_hints;
                            inlay_hints_enabled = new_inlay_hints_enabled;
                            supports_inlay_hints = new_supports_inlay_hints;
                            if should_notify {
                                cx.notify()
                            }
                        }));
                    ToolbarItemLocation::PrimaryRight
                } else {
                    ToolbarItemLocation::Hidden
                }
            }
            None => {
                self.active_item = None;
                ToolbarItemLocation::Hidden
            }
        }
    }
}
