//! Popconfirm module.
//!
//! This public module implements the Liora popover confirmation component for guarded actions. It keeps the reusable
//! component logic inside `liora-components` rather than host applications so
//! downstream GPUI applications can compose the same behavior with their own
//! app state, assets, and release policy.
//!
//! ## Usage model
//!
//! Components in this module render native GPUI element trees. Stateless builder
//! values can be constructed inline, while controls with focus, selection,
//! popup, drag, or editing state should be stored as `gpui::Entity<T>` fields in
//! the parent view so state survives GPUI render passes.
//!
//! ## Design contract
//!
//! The implementation should use Liora theme tokens from `liora-core` and
//! `liora-theme`, keep accessibility-oriented keyboard/pointer behavior close to
//! the component, and avoid app-specific host-application resources in this SDK
//! crate.

use crate::{Button, Popover};
use gpui::{
    AnyElement, App, Component, IntoElement, RenderOnce, SharedString, Window, div, prelude::*,
};
use liora_core::{Config, Placement, clear_popover};
use liora_icons::Icon;
use liora_icons_lucide::IconName;
use std::sync::Arc;

/// Fluent native GPUI component for rendering Liora popconfirm.
pub struct Popconfirm {
    trigger: AnyElement,
    title: SharedString,
    confirm_text: SharedString,
    cancel_text: SharedString,
    on_confirm: Option<Arc<dyn Fn(&mut Window, &mut App) + 'static>>,
    on_cancel: Option<Arc<dyn Fn(&mut Window, &mut App) + 'static>>,
    placement: Placement,
    close_on_click_outside: bool,
    close_on_escape: bool,
    trigger_id: Option<SharedString>,
}

impl Popconfirm {
    /// Creates `Popconfirm` initialized from the supplied trigger.
    pub fn new(trigger: impl IntoElement) -> Self {
        Self {
            trigger: trigger.into_any_element(),
            title: SharedString::default(),
            confirm_text: "Confirm".into(),
            cancel_text: "Cancel".into(),
            on_confirm: None,
            on_cancel: None,
            placement: Placement::Top,
            close_on_click_outside: true,
            close_on_escape: true,
            trigger_id: None,
        }
    }

    /// Sets the primary title text displayed by the component.
    pub fn title(mut self, title: impl Into<SharedString>) -> Self {
        self.title = title.into();
        self
    }

    /// Sets the confirm text value used by the component.
    pub fn confirm_text(mut self, text: impl Into<SharedString>) -> Self {
        self.confirm_text = text.into();
        self
    }

    /// Sets the cancel text value used by the component.
    pub fn cancel_text(mut self, text: impl Into<SharedString>) -> Self {
        self.cancel_text = text.into();
        self
    }

    /// Registers a callback that runs when confirm occurs.
    pub fn on_confirm(mut self, f: impl Fn(&mut Window, &mut App) + 'static) -> Self {
        self.on_confirm = Some(Arc::new(f));
        self
    }

    /// Registers a callback that runs when cancel occurs.
    pub fn on_cancel(mut self, f: impl Fn(&mut Window, &mut App) + 'static) -> Self {
        self.on_cancel = Some(Arc::new(f));
        self
    }

    /// Selects the popup, label, or overlay placement.
    pub fn placement(mut self, placement: Placement) -> Self {
        self.placement = placement;
        self
    }

    /// Assigns a stable element id used by GPUI state, hit testing, and automated interaction tests.
    pub fn id(mut self, id: impl Into<SharedString>) -> Self {
        self.trigger_id = Some(id.into());
        self
    }

    /// Toggles whether the popup closes when escape occurs.
    pub fn close_on_escape(mut self, close: bool) -> Self {
        self.close_on_escape = close;
        self
    }

    /// Toggles whether the popup closes when click outside occurs.
    pub fn close_on_click_outside(mut self, close: bool) -> Self {
        self.close_on_click_outside = close;
        self
    }
}

impl RenderOnce for Popconfirm {
    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
        let title = self.title.clone();
        let confirm_text = self.confirm_text.clone();
        let cancel_text = self.cancel_text.clone();
        let on_confirm = self.on_confirm.clone();
        let on_cancel = self.on_cancel.clone();
        let close_on_click_outside = self.close_on_click_outside;
        let close_on_escape = self.close_on_escape;
        let theme = cx.global::<Config>().theme.clone();
        let trigger_id = self
            .trigger_id
            .clone()
            .unwrap_or_else(|| format!("popconfirm-trigger-{}", title).into());
        let popover_id = trigger_id.clone();

        Popover::new(self.trigger)
            .id(trigger_id)
            .placement(self.placement)
            .flush_content()
            .close_on_click_outside(close_on_click_outside)
            .close_on_escape(close_on_escape)
            .content(move |_window, _cx| {
                let on_confirm = on_confirm.clone();
                let on_cancel = on_cancel.clone();
                let confirm_text = confirm_text.clone();
                let cancel_text = cancel_text.clone();
                let cancel_popover_id = popover_id.clone();
                let confirm_popover_id = popover_id.clone();

                div()
                    .p_4()
                    .flex()
                    .flex_col()
                    .gap_3()
                    .child(
                        div()
                            .flex()
                            .flex_row()
                            .items_center()
                            .gap_2()
                            .child(
                                div().flex_none().text_color(theme.warning.base).child(
                                    Icon::new(IconName::TriangleAlert)
                                        .size(gpui::px(16.0))
                                        .color(theme.warning.base),
                                ),
                            )
                            .child(
                                div()
                                    .min_w(gpui::px(0.0))
                                    .font_weight(gpui::FontWeight::BOLD)
                                    .child(title.clone()),
                            ),
                    )
                    .child(
                        div()
                            .flex()
                            .flex_row()
                            .justify_end()
                            .gap_2()
                            .child(Button::new(cancel_text.clone()).small().on_click(
                                move |_event, _window, _cx| {
                                    if let Some(ref f) = on_cancel {
                                        f(_window, _cx);
                                    }
                                    clear_popover(&cancel_popover_id, _cx);
                                },
                            ))
                            .child(
                                Button::new(confirm_text.clone())
                                    .primary()
                                    .small()
                                    .on_click(move |_event, _window, _cx| {
                                        if let Some(ref f) = on_confirm {
                                            f(_window, _cx);
                                        }
                                        clear_popover(&confirm_popover_id, _cx);
                                    }),
                            ),
                    )
            })
    }
}

impl IntoElement for Popconfirm {
    type Element = Component<Self>;
    fn into_element(self) -> Self::Element {
        Component::new(self)
    }
}

#[cfg(test)]
mod padding_regression_tests {
    #[test]
    fn popconfirm_flushes_shared_popover_padding_for_confirm_panel() {
        let source = include_str!("popconfirm.rs")
            .split("#[cfg(test)]")
            .next()
            .unwrap();

        assert!(source.contains(".flush_content()"));
    }

    #[test]
    fn popconfirm_uses_fixed_native_icon_instead_of_emoji_glyph() {
        let source = include_str!("popconfirm.rs")
            .split("#[cfg(test)]")
            .next()
            .unwrap();

        assert!(source.contains("IconName::TriangleAlert"));
        assert!(source.contains("Icon::new"));
        assert!(source.contains(".flex_none()"));
        assert!(!source.contains("⚠️"));
    }
}
