// This module provides infrastructure for showing onboarding banners in the title bar.
// It's currently not in use but is kept for future feature announcements.
#![allow(dead_code)]

use gpui::{Action, Entity, Global, Render, SharedString, TaskExt};
use ui::{ButtonLike, Tooltip, prelude::*};
use util::ResultExt;

/// Prompts the user to try newly released Zed's features
pub struct OnboardingBanner {
    dismissed: bool,
    source: String,
    details: BannerDetails,
    visible_when: Option<Box<dyn Fn(&mut App) -> bool>>,
}

#[derive(Clone)]
struct BannerGlobal {
    entity: Entity<OnboardingBanner>,
}
impl Global for BannerGlobal {}

pub struct BannerDetails {
    pub action: Box<dyn Action>,
    pub icon_name: IconName,
    pub label: SharedString,
    pub subtitle: Option<SharedString>,
}

impl OnboardingBanner {
    pub fn new(
        source: &str,
        icon_name: IconName,
        label: impl Into<SharedString>,
        subtitle: Option<SharedString>,
        action: Box<dyn Action>,
        cx: &mut Context<Self>,
    ) -> Self {
        cx.set_global(BannerGlobal {
            entity: cx.entity(),
        });
        Self {
            source: source.to_string(),
            details: BannerDetails {
                action,
                icon_name,
                label: label.into(),
                subtitle: subtitle.or(Some(SharedString::from("Introducing:"))),
            },
            visible_when: None,
            dismissed: get_dismissed(source, cx),
        }
    }

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

    fn should_show(&self, cx: &mut App) -> bool {
        !self.dismissed && self.visible_when.as_ref().map_or(true, |f| f(cx))
    }

    fn dismiss(&mut self, cx: &mut Context<Self>) {
        persist_dismissed(&self.source, cx);
        self.dismissed = true;
        cx.notify();
    }
}

fn dismissed_at_key(source: &str) -> String {
    if source == "Git Onboarding" {
        "zed_git_banner_dismissed_at".to_string()
    } else {
        format!(
            "{}_banner_dismissed_at",
            source.to_lowercase().trim().replace(" ", "_")
        )
    }
}

fn get_dismissed(source: &str, cx: &App) -> bool {
    let dismissed_at = dismissed_at_key(source);
    db::kvp::KeyValueStore::global(cx)
        .read_kvp(&dismissed_at)
        .log_err()
        .is_some_and(|dismissed| dismissed.is_some())
}

fn persist_dismissed(source: &str, cx: &mut App) {
    let dismissed_at = dismissed_at_key(source);
    let kvp = db::kvp::KeyValueStore::global(cx);
    cx.spawn(async move |_| {
        let time = chrono::Utc::now().to_rfc3339();
        kvp.write_kvp(dismissed_at, time).await
    })
    .detach_and_log_err(cx);
}

pub fn restore_banner(cx: &mut App) {
    if let Some(banner_global) = cx.try_global::<BannerGlobal>() {
        let entity = banner_global.entity.clone();
        cx.defer(move |cx| {
            entity.update(cx, |this, cx| {
                this.dismissed = false;
                cx.notify();
            });
        });

        let source = &cx.global::<BannerGlobal>().entity.read(cx).source;
        let dismissed_at = dismissed_at_key(source);
        let kvp = db::kvp::KeyValueStore::global(cx);
        cx.spawn(async move |_| kvp.delete_kvp(dismissed_at).await)
            .detach_and_log_err(cx);
    }
}

impl Render for OnboardingBanner {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        if !self.should_show(cx) {
            return div();
        }

        let border_color = cx.theme().colors().editor_foreground.opacity(0.3);
        let banner = h_flex()
            .rounded_sm()
            .border_1()
            .border_color(border_color)
            .occlude()
            .child(
                ButtonLike::new("try-a-feature")
                    .child(
                        h_flex()
                            .h_full()
                            .gap_1()
                            .child(Icon::new(self.details.icon_name).size(IconSize::XSmall))
                            .child(
                                h_flex()
                                    .gap_0p5()
                                    .when_some(self.details.subtitle.as_ref(), |this, subtitle| {
                                        this.child(
                                            Label::new(subtitle)
                                                .size(LabelSize::Small)
                                                .color(Color::Muted),
                                        )
                                    })
                                    .child(Label::new(&self.details.label).size(LabelSize::Small)),
                            ),
                    )
                    .on_click(cx.listener(|this, _, window, cx| {
                        telemetry::event!("Banner Clicked", source = this.source);
                        this.dismiss(cx);
                        window.dispatch_action(this.details.action.boxed_clone(), cx)
                    })),
            )
            .child(
                div().border_l_1().border_color(border_color).child(
                    IconButton::new("close", IconName::Close)
                        .icon_size(IconSize::Indicator)
                        .on_click(cx.listener(|this, _, _window, cx| {
                            telemetry::event!("Banner Dismissed", source = this.source);
                            this.dismiss(cx)
                        }))
                        .tooltip(|_window, cx| {
                            Tooltip::with_meta(
                                "Close Announcement Banner",
                                None,
                                "It won't show again for this feature",
                                cx,
                            )
                        }),
                ),
            );

        div().pr_2().child(banner)
    }
}
