mod button_group;
mod theme;

#[cfg(not(feature = "ssr"))]
use crate::utils::dyn_classes;
use crate::{
    components::{OptionComp, Wave, WaveRef},
    icon::*,
    theme::*,
    utils::{mount_style, ssr_class, ComponentRef},
};
pub use button_group::ButtonGroup;
use leptos::*;
pub use theme::ButtonTheme;

#[derive(Default, PartialEq, Clone, Copy)]
pub enum ButtonVariant {
    #[default]
    Primary,
    Solid,
    Text,
    Link,
}

#[derive(Default, Clone)]
pub enum ButtonColor {
    #[default]
    Primary,
    Success,
    Warning,
    Error,
}

impl ButtonColor {
    fn theme_color(&self, theme: &Theme) -> String {
        match self {
            ButtonColor::Primary => theme.common.color_primary.clone(),
            ButtonColor::Success => theme.common.color_success.clone(),
            ButtonColor::Warning => theme.common.color_warning.clone(),
            ButtonColor::Error => theme.common.color_error.clone(),
        }
    }
    fn theme_color_hover(&self, theme: &Theme) -> String {
        match self {
            ButtonColor::Primary => theme.common.color_primary_hover.clone(),
            ButtonColor::Success => theme.common.color_success_hover.clone(),
            ButtonColor::Warning => theme.common.color_warning_hover.clone(),
            ButtonColor::Error => theme.common.color_error_hover.clone(),
        }
    }

    fn theme_color_active(&self, theme: &Theme) -> String {
        match self {
            ButtonColor::Primary => theme.common.color_primary_active.clone(),
            ButtonColor::Success => theme.common.color_success_active.clone(),
            ButtonColor::Warning => theme.common.color_warning_active.clone(),
            ButtonColor::Error => theme.common.color_error_active.clone(),
        }
    }
}

#[derive(Default, Clone)]
pub enum ButtonSize {
    Tiny,
    Small,
    #[default]
    Medium,
    Large,
}

impl ButtonSize {
    fn theme_font_size(&self, theme: &Theme) -> String {
        match self {
            ButtonSize::Tiny => theme.common.font_size_tiny.clone(),
            ButtonSize::Small => theme.common.font_size_small.clone(),
            ButtonSize::Medium => theme.common.font_size_medium.clone(),
            ButtonSize::Large => theme.common.font_size_large.clone(),
        }
    }

    fn theme_height(&self, theme: &Theme) -> String {
        match self {
            ButtonSize::Tiny => theme.common.height_tiny.clone(),
            ButtonSize::Small => theme.common.height_small.clone(),
            ButtonSize::Medium => theme.common.height_medium.clone(),
            ButtonSize::Large => theme.common.height_large.clone(),
        }
    }

    fn theme_padding(&self, theme: &Theme) -> String {
        match self {
            ButtonSize::Tiny => theme.button.padding_tiny.clone(),
            ButtonSize::Small => theme.button.padding_small.clone(),
            ButtonSize::Medium => theme.button.padding_medium.clone(),
            ButtonSize::Large => theme.button.padding_large.clone(),
        }
    }
}

#[component]
pub fn Button(
    #[prop(optional, into)] style: MaybeSignal<String>,
    #[prop(optional, into)] class: MaybeSignal<String>,
    #[prop(optional, into)] variant: MaybeSignal<ButtonVariant>,
    #[prop(optional, into)] color: MaybeSignal<ButtonColor>,
    #[prop(optional, into)] size: MaybeSignal<ButtonSize>,
    #[prop(optional, into)] round: MaybeSignal<bool>,
    #[prop(optional, into)] circle: MaybeSignal<bool>,
    #[prop(optional, into)] icon: Option<Icon>,
    #[prop(optional, into)] loading: MaybeSignal<bool>,
    #[prop(optional, into)] disabled: MaybeSignal<bool>,
    #[prop(optional, into)] on_click: Option<Callback<ev::MouseEvent>>,
    #[prop(optional)] children: Option<Children>,
) -> impl IntoView {
    let theme = use_theme(Theme::light);
    let css_vars = create_memo(move |_| {
        let mut css_vars = String::new();
        theme.with(|theme| {
            let bg_color = color.get().theme_color(theme);
            css_vars.push_str(&format!(
                "--thaw-font-color-disabled: {};",
                theme.button.color_text_disabled
            ));
            css_vars.push_str(&format!(
                "--thaw-font-size: {};",
                size.get().theme_font_size(theme)
            ));
            css_vars.push_str(&format!(
                "--thaw-height: {};",
                size.get().theme_height(theme)
            ));
            css_vars.push_str(&format!(
                "--thaw-padding: {};",
                size.get().theme_padding(theme)
            ));

            match variant.get() {
                ButtonVariant::Primary => {
                    let bg_color_hover = color.get().theme_color_hover(theme);
                    let bg_color_active = color.get().theme_color_active(theme);
                    css_vars.push_str(&format!("--thaw-background-color: {bg_color};"));
                    css_vars.push_str(&format!("--thaw-background-color-hover: {bg_color_hover};"));
                    css_vars.push_str(&format!(
                        "--thaw-background-color-active: {bg_color_active};"
                    ));
                    css_vars.push_str("--thaw-font-color: #fff;");
                    css_vars.push_str(&format!("--thaw-border-color: {bg_color};"));
                    css_vars.push_str(&format!("--thaw-border-color-hover: {bg_color};"));
                    css_vars.push_str(&format!("--thaw-ripple-color: {bg_color};"));

                    css_vars.push_str(&format!(
                        "--thaw-background-color-disabled: {};",
                        theme.button.color_background_disabled
                    ));
                    css_vars.push_str(&format!(
                        "--thaw-border-color-disabled: {};",
                        theme.button.color_border_disabled
                    ));
                }
                ButtonVariant::Solid => {
                    css_vars.push_str(&format!("--thaw-font-color-hover: {bg_color};"));
                    css_vars.push_str(&format!(
                        "--thaw-border-color: {};",
                        theme.button.border_color_solid
                    ));
                    css_vars.push_str(&format!("--thaw-border-color-hover: {bg_color};"));
                    css_vars.push_str(&format!("--thaw-ripple-color: {bg_color};"));
                    css_vars.push_str(&format!(
                        "--thaw-border-color-disabled: {};",
                        theme.button.color_border_disabled
                    ));
                }
                ButtonVariant::Text => {
                    css_vars.push_str(&format!("--thaw-font-color-hover: {bg_color};"));
                    css_vars.push_str(&format!(
                        "--thaw-background-color-hover: {};",
                        theme.button.color_text_hover
                    ));
                    css_vars.push_str(&format!(
                        "--thaw-background-color-active: {};",
                        theme.button.color_text_active
                    ));
                    css_vars.push_str("--thaw-ripple-color: #0000;");
                }
                ButtonVariant::Link => {
                    css_vars.push_str(&format!("--thaw-font-color-hover: {bg_color};"));
                    css_vars.push_str("--thaw-ripple-color: #0000;");
                }
            }
        });

        css_vars
    });
    mount_style("button", include_str!("./button.css"));

    let icon_style = if children.is_some() {
        "margin-right: 6px"
    } else {
        ""
    };

    let disabled = create_memo(move |_| {
        if loading.get() {
            return true;
        }

        disabled.get()
    });

    let wave_ref = ComponentRef::<WaveRef>::default();

    let on_click = move |event| {
        if disabled.get() {
            return;
        }
        if let Some(wave_ref) = wave_ref.get_untracked() {
            wave_ref.play();
        }
        let Some(callback) = on_click.as_ref() else {
            return;
        };
        callback.call(event);
    };

    let ssr_class = ssr_class(&class);
    view! {
        <button
            class=ssr_class
            use:dyn_classes=class
            class:thaw-button=true
            class=("thaw-button--solid", move || variant.get() == ButtonVariant::Solid)
            class=("thaw-button--text", move || variant.get() == ButtonVariant::Text)
            class=("thaw-button--link", move || variant.get() == ButtonVariant::Link)
            class=("thaw-button--round", move || round.get())
            class=("thaw-button--circle", move || circle.get())
            class=("thaw-button--disabled", move || disabled.get())
            style=move || format!("{}{}", css_vars.get(), style.get())
            disabled=move || disabled.get()
            on:click=on_click
        >
            <Wave comp_ref=wave_ref/>
            {move || {
                if loading.get() {
                    view! {
                        <Icon
                            icon=Icon::from(AiIcon::AiLoadingOutlined)
                            style=format!(
                                "animation: thawLoadingCircle 1s infinite linear;{icon_style}",
                            )
                        />
                    }
                        .into()
                } else if let Some(icon) = icon {
                    view! { <Icon icon=icon style=icon_style/> }.into()
                } else {
                    None
                }
            }}

            <OptionComp value=children let:children>
                {children()}
            </OptionComp>
        </button>
    }
}