use gpui::{
    elements::{Empty, Label},
    AnyElement, Element, Entity, Subscription, View, ViewContext,
};
use settings::SettingsStore;
use workspace::{item::ItemHandle, StatusItemView};

use crate::{state::Mode, Vim, VimEvent, VimModeSetting};

pub struct ModeIndicator {
    pub mode: Option<Mode>,
    _subscription: Subscription,
}

impl ModeIndicator {
    pub fn new(cx: &mut ViewContext<Self>) -> Self {
        let handle = cx.handle().downgrade();

        let _subscription = cx.subscribe_global::<VimEvent, _>(move |&event, cx| {
            if let Some(mode_indicator) = handle.upgrade(cx) {
                match event {
                    VimEvent::ModeChanged { mode } => {
                        mode_indicator.window().update(cx, |cx| {
                            mode_indicator.update(cx, move |mode_indicator, cx| {
                                mode_indicator.set_mode(mode, cx);
                            })
                        });
                    }
                }
            }
        });

        cx.observe_global::<SettingsStore, _>(move |mode_indicator, cx| {
            if settings::get::<VimModeSetting>(cx).0 {
                mode_indicator.mode = cx
                    .has_global::<Vim>()
                    .then(|| cx.global::<Vim>().state().mode);
            } else {
                mode_indicator.mode.take();
            }
        })
        .detach();

        // Vim doesn't exist in some tests
        let mode = cx
            .has_global::<Vim>()
            .then(|| {
                let vim = cx.global::<Vim>();
                vim.enabled.then(|| vim.state().mode)
            })
            .flatten();

        Self {
            mode,
            _subscription,
        }
    }

    pub fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
        if self.mode != Some(mode) {
            self.mode = Some(mode);
            cx.notify();
        }
    }
}

impl Entity for ModeIndicator {
    type Event = ();
}

impl View for ModeIndicator {
    fn ui_name() -> &'static str {
        "ModeIndicatorView"
    }

    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
        let Some(mode) = self.mode.as_ref() else {
            return Empty::new().into_any();
        };

        let theme = &theme::current(cx).workspace.status_bar;

        let text = match mode {
            Mode::Normal => "-- NORMAL --",
            Mode::Insert => "-- INSERT --",
            Mode::Visual => "-- VISUAL --",
            Mode::VisualLine => "-- VISUAL LINE --",
            Mode::VisualBlock => "-- VISUAL BLOCK --",
        };
        Label::new(text, theme.vim_mode_indicator.text.clone())
            .contained()
            .with_style(theme.vim_mode_indicator.container)
            .into_any()
    }
}

impl StatusItemView for ModeIndicator {
    fn set_active_pane_item(
        &mut self,
        _active_pane_item: Option<&dyn ItemHandle>,
        _cx: &mut ViewContext<Self>,
    ) {
        // nothing to do.
    }
}
