use std::sync::Arc;

use gpui::{AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent};

use crate::{Disclosure, prelude::*};

#[derive(IntoElement, RegisterComponent)]
pub struct TreeViewItem {
    id: ElementId,
    group_name: Option<SharedString>,
    label: SharedString,
    expanded: bool,
    selected: bool,
    disabled: bool,
    focused: bool,
    default_expanded: bool,
    root_item: bool,
    tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>>,
    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
    on_hover: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
    on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
    on_secondary_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut Window, &mut App) + 'static>>,
    tab_index: Option<isize>,
    focus_handle: Option<gpui::FocusHandle>,
}

impl TreeViewItem {
    pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
        Self {
            id: id.into(),
            group_name: None,
            label: label.into(),
            expanded: false,
            selected: false,
            disabled: false,
            focused: false,
            default_expanded: false,
            root_item: false,
            tooltip: None,
            on_click: None,
            on_hover: None,
            on_toggle: None,
            on_secondary_mouse_down: None,
            tab_index: None,
            focus_handle: None,
        }
    }

    pub fn group_name(mut self, group_name: impl Into<SharedString>) -> Self {
        self.group_name = Some(group_name.into());
        self
    }

    pub fn on_click(
        mut self,
        handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
    ) -> Self {
        self.on_click = Some(Box::new(handler));
        self
    }

    pub fn on_hover(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
        self.on_hover = Some(Box::new(handler));
        self
    }

    pub fn on_secondary_mouse_down(
        mut self,
        handler: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
    ) -> Self {
        self.on_secondary_mouse_down = Some(Box::new(handler));
        self
    }

    pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
        self.tooltip = Some(Box::new(tooltip));
        self
    }

    pub fn tab_index(mut self, tab_index: isize) -> Self {
        self.tab_index = Some(tab_index);
        self
    }

    pub fn expanded(mut self, toggle: bool) -> Self {
        self.expanded = toggle;
        self
    }

    pub fn default_expanded(mut self, default_expanded: bool) -> Self {
        self.default_expanded = default_expanded;
        self
    }

    pub fn on_toggle(
        mut self,
        on_toggle: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
    ) -> Self {
        self.on_toggle = Some(Arc::new(on_toggle));
        self
    }

    pub fn root_item(mut self, root_item: bool) -> Self {
        self.root_item = root_item;
        self
    }

    pub fn focused(mut self, focused: bool) -> Self {
        self.focused = focused;
        self
    }

    pub fn track_focus(mut self, focus_handle: &gpui::FocusHandle) -> Self {
        self.focus_handle = Some(focus_handle.clone());
        self
    }
}

impl Disableable for TreeViewItem {
    fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }
}

impl Toggleable for TreeViewItem {
    fn toggle_state(mut self, selected: bool) -> Self {
        self.selected = selected;
        self
    }
}

impl RenderOnce for TreeViewItem {
    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
        let selected_bg = cx.theme().colors().element_active.opacity(0.5);

        let transparent_border = cx.theme().colors().border.opacity(0.);
        let selected_border = cx.theme().colors().border.opacity(0.4);
        let focused_border = cx.theme().colors().border_focused;

        let item_size = rems_from_px(28.);
        let indentation_line = h_flex()
            .h(item_size)
            .w(px(22.))
            .flex_none()
            .justify_center()
            .child(
                div()
                    .w_px()
                    .h_full()
                    .bg(cx.theme().colors().border.opacity(0.5)),
            );

        h_flex()
            .id(self.id)
            .when_some(self.group_name, |this, group| this.group(group))
            .w_full()
            .child(
                h_flex()
                    .id("inner_tree_view_item")
                    .cursor_pointer()
                    .size_full()
                    .h(item_size)
                    .pl_0p5()
                    .pr_1()
                    .gap_2()
                    .rounded_sm()
                    .border_1()
                    .border_color(transparent_border)
                    .focus_visible(|s| s.border_color(focused_border))
                    .when(self.selected, |this| {
                        this.border_color(selected_border).bg(selected_bg)
                    })
                    .hover(|s| s.bg(cx.theme().colors().element_hover))
                    .map(|this| {
                        let label = self.label;

                        if self.root_item {
                            this.child(
                                Disclosure::new("toggle", self.expanded)
                                    .when_some(self.on_toggle.clone(), |disclosure, on_toggle| {
                                        disclosure.on_toggle_expanded(on_toggle)
                                    })
                                    .opened_icon(IconName::ChevronDown)
                                    .closed_icon(IconName::ChevronRight),
                            )
                            .child(
                                Label::new(label)
                                    .when(!self.selected, |this| this.color(Color::Muted)),
                            )
                        } else {
                            this.child(indentation_line).child(
                                h_flex()
                                    .id("nested_inner_tree_view_item")
                                    .w_full()
                                    .flex_grow()
                                    .child(
                                        Label::new(label)
                                            .when(!self.selected, |this| this.color(Color::Muted)),
                                    ),
                            )
                        }
                    })
                    .when_some(self.focus_handle, |this, handle| this.track_focus(&handle))
                    .when_some(self.tab_index, |this, index| this.tab_index(index))
                    .when_some(self.on_hover, |this, on_hover| this.on_hover(on_hover))
                    .when_some(
                        self.on_click.filter(|_| !self.disabled),
                        |this, on_click| this.on_click(on_click),
                    )
                    .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
                        this.on_mouse_down(MouseButton::Right, move |event, window, cx| {
                            (on_mouse_down)(event, window, cx)
                        })
                    })
                    .when_some(self.tooltip, |this, tooltip| this.tooltip(tooltip)),
            )
    }
}

impl Component for TreeViewItem {
    fn scope() -> ComponentScope {
        ComponentScope::Navigation
    }

    fn description() -> Option<&'static str> {
        Some(
            "A hierarchical list of items that may have a parent-child relationship where children can be toggled into view by expanding or collapsing their parent item.",
        )
    }

    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
        let container = || {
            v_flex()
                .p_2()
                .w_64()
                .border_1()
                .border_color(cx.theme().colors().border_variant)
                .bg(cx.theme().colors().panel_background)
        };

        Some(
            example_group(vec![
                single_example(
                    "Basic Tree View",
                    container()
                        .child(
                            TreeViewItem::new("index-1", "Tree Item Root #1")
                                .root_item(true)
                                .toggle_state(true),
                        )
                        .child(TreeViewItem::new("index-2", "Tree Item #2"))
                        .child(TreeViewItem::new("index-3", "Tree Item #3"))
                        .child(TreeViewItem::new("index-4", "Tree Item Root #2").root_item(true))
                        .child(TreeViewItem::new("index-5", "Tree Item #5"))
                        .child(TreeViewItem::new("index-6", "Tree Item #6"))
                        .into_any_element(),
                ),
                single_example(
                    "Active Child",
                    container()
                        .child(TreeViewItem::new("index-1", "Tree Item Root #1").root_item(true))
                        .child(TreeViewItem::new("index-2", "Tree Item #2").toggle_state(true))
                        .child(TreeViewItem::new("index-3", "Tree Item #3"))
                        .into_any_element(),
                ),
                single_example(
                    "Focused Parent",
                    container()
                        .child(
                            TreeViewItem::new("index-1", "Tree Item Root #1")
                                .root_item(true)
                                .focused(true)
                                .toggle_state(true),
                        )
                        .child(TreeViewItem::new("index-2", "Tree Item #2"))
                        .child(TreeViewItem::new("index-3", "Tree Item #3"))
                        .into_any_element(),
                ),
                single_example(
                    "Focused Child",
                    container()
                        .child(
                            TreeViewItem::new("index-1", "Tree Item Root #1")
                                .root_item(true)
                                .toggle_state(true),
                        )
                        .child(TreeViewItem::new("index-2", "Tree Item #2").focused(true))
                        .child(TreeViewItem::new("index-3", "Tree Item #3"))
                        .into_any_element(),
                ),
            ])
            .into_any_element(),
        )
    }
}
