///|
pub(all) enum ButtonVariant {
  Primary
  Secondary
  Ghost
} derive(Debug, Eq)

///|
pub extend ButtonVariant with Eq::{equal, not_equal}

///|
pub extend ButtonVariant with Debug::{to_repr}

///|
pub(all) enum ButtonSize {
  Default
  Sm
  Icon
  IconSm
} derive(Debug, Eq)

///|
pub extend ButtonSize with Eq::{equal, not_equal}

///|
pub extend ButtonSize with Debug::{to_repr}

///|
const ButtonBaseStyle : String = "position:relative;display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;gap:4px;margin:0;border:none;border-radius:var(--sumi-radius,8px);font-size:13px;line-height:22px;font-weight:500;white-space:nowrap;cursor:pointer;user-select:none;appearance:none;-webkit-appearance:none;outline:none;vertical-align:middle"

///|
const ButtonPrimaryStyle : String = "padding:4px 16px;height:32px;background:var(--sumi-primary-bg,#fafafa);color:var(--sumi-primary-fg,#1a1a1a)"

///|
const ButtonPrimaryDisabledStyle : String = "background:var(--sumi-primary-bg-disabled,rgba(204,221,255,0.16));color:var(--sumi-primary-fg-disabled,rgba(224,245,255,0.2))"

///|
const ButtonSecondaryStyle : String = "padding:4px 16px;height:32px;background:var(--sumi-block,rgba(255,255,255,0.08));color:var(--sumi-text,#ffffff)"

///|
const ButtonGhostStyle : String = "padding:4px 8px;min-height:32px;background:transparent;color:var(--sumi-text,#ffffff)"

///|
const ButtonSmStyle : String = "padding:3px 12px;height:28px;font-size:12px;line-height:20px"

///|
const ButtonIconStyle : String = "padding:0;width:32px;height:32px"

///|
const ButtonIconSmStyle : String = "padding:0;width:28px;height:28px"

///|
fn button_variant_value(variant : ButtonVariant) -> String {
  match variant {
    Primary => "primary"
    Secondary => "secondary"
    Ghost => "ghost"
  }
}

///|
fn button_size_value(size : ButtonSize) -> String {
  match size {
    Default => "default"
    Sm => "sm"
    Icon => "icon"
    IconSm => "icon-sm"
  }
}

///|
fn button_variant_style(
  variant : ButtonVariant,
  disabled : Bool,
  pressed : Bool,
) -> String {
  match variant {
    Primary =>
      if disabled {
        ButtonPrimaryStyle + ";" + ButtonPrimaryDisabledStyle
      } else {
        ButtonPrimaryStyle
      }
    Secondary =>
      if disabled {
        ButtonSecondaryStyle +
        ";color:var(--sumi-text-disabled,rgba(255,255,255,0.2))"
      } else if pressed {
        ButtonSecondaryStyle +
        ";background:var(--sumi-block,rgba(255,255,255,0.08))"
      } else {
        ButtonSecondaryStyle
      }
    Ghost =>
      if disabled {
        ButtonGhostStyle +
        ";color:var(--sumi-text-disabled,rgba(255,255,255,0.2))"
      } else if pressed {
        ButtonGhostStyle +
        ";background:var(--sumi-block,rgba(255,255,255,0.08))"
      } else {
        ButtonGhostStyle
      }
  }
}

///|
fn button_size_style(variant : ButtonVariant, size : ButtonSize) -> String {
  match size {
    Default => ""
    Sm =>
      match variant {
        // Ghost sm is the menu-trigger size: keep the compact 8px gutter.
        Ghost => "height:28px;font-size:12px;line-height:20px"
        Primary | Secondary => ButtonSmStyle
      }
    Icon => ButtonIconStyle + ";padding:0"
    IconSm => ButtonIconSmStyle
  }
}

///|
/// Text buttons hold an 80px minimum so short labels ("OK", "Apply") keep
/// the design's standard footprint; ghost and icon sizes hug their content.
fn button_min_width_style(variant : ButtonVariant, size : ButtonSize) -> String {
  match variant {
    Ghost => ""
    Primary | Secondary =>
      match size {
        Default | Sm => "min-width:80px"
        Icon | IconSm => ""
      }
  }
}

///|
/// Render a button in one of the design's three variants.
///
/// * `Primary` — solid light fill with dark text, the main call to action.
/// * `Secondary` — translucent white fill.
/// * `Ghost` — transparent, fills on hover; the toolbar/menu-button look.
///
/// `pressed` marks a toggle-style button as the currently active tool: it
/// adds `aria-pressed` and the design's selected fill (same tone as hover).
///
/// `loading=true` keeps the default fill and the button's width: the content
/// stays rendered but invisible while a 16px spinner overlays its center, and
/// `aria-busy` is set; pass a `title` describing the busy action for screen
/// readers.
///
/// User styles are appended last and therefore override the built-in recipe.
pub fn[C : @html.IsChildren] button(
  variant? : ButtonVariant = Primary,
  size? : ButtonSize = Default,
  disabled? : Bool = false,
  pressed? : Bool = false,
  loading? : Bool = false,
  type_? : String = "button",
  id? : String,
  class? : String,
  title? : String,
  name? : String,
  value? : String,
  autofocus? : Bool,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_attrs = @internal.sumi_attrs(attrs)
    .data_set("slot", "button")
    .data_set("variant", button_variant_value(variant))
    .data_set("size", button_size_value(size))
  if disabled {
    ignore(element_attrs.aria_disabled("true"))
  }
  if pressed {
    ignore(element_attrs.aria_pressed(@internal.sumi_bool(true)))
  }
  if loading {
    ignore(element_attrs.aria_busy("true"))
  }
  let content : @html.Html = if loading {
    @html.fragment([
      @html.span(
        style=[
          "display:inline-flex;align-items:center;justify-content:center;gap:4px;visibility:hidden",
        ],
        attrs=@html.Attrs::build().aria_hidden("true"),
        [@internal.sumi_slot(children)],
      ),
      @html.span(
        style=[
          "position:absolute;inset:0;display:inline-flex;align-items:center;justify-content:center",
        ],
        [spinner(size=16)],
      ),
    ])
  } else {
    @internal.sumi_slot(children)
  }
  @html.button(
    style=@internal.sumi_styles(
      [
        @internal.SumiBoxSizing,
        @internal.SumiFontSans,
        @internal.SumiTextRendering,
        @internal.SumiTransition,
        ButtonBaseStyle,
        button_variant_style(variant, disabled, pressed),
        button_size_style(variant, size),
        button_min_width_style(variant, size),
        @internal.sumi_disabled_style(disabled || loading),
      ],
      style,
    ),
    id?,
    class?,
    title?,
    type_~,
    disabled~,
    name?,
    value?,
    autofocus?,
    on_click?,
    attrs=element_attrs,
    [content],
  )
}

///|
/// A square button for a single icon. Convenience wrapper over `button` with
/// `size=Icon`/`IconSm` and `variant=Ghost` by default.
pub fn icon_button(
  icon : @html.Html,
  variant? : ButtonVariant = Ghost,
  size? : ButtonSize = Icon,
  disabled? : Bool = false,
  pressed? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  let element_attrs = @internal.sumi_attrs(attrs)
    .data_set("slot", "icon-button")
    .data_set("state", if pressed { "on" } else { "off" })
  if pressed {
    ignore(element_attrs.aria_pressed(@internal.sumi_bool(true)))
  }
  if aria_label is Some(label) {
    ignore(element_attrs.aria_label(label))
  }
  button(
    variant~,
    size~,
    disabled~,
    pressed~,
    id?,
    class?,
    title?,
    on_click?,
    attrs=element_attrs,
    style~,
    [icon],
  )
}