///|
/// Explicit scope passed by [`alert_dialog`] to its compound parts.
struct AlertDialogScope(DialogScope)

///|
const AlertDialogContentStyle : String = "width:min(100%,32rem)"

///|
const AlertDialogMediaStyle : String = "display:inline-flex;width:4rem;height:4rem;flex-shrink:0;align-items:center;justify-content:center;margin-bottom:0.5rem;border-radius:calc(var(--rui-radius,0.625rem) - 0.125rem);background:var(--rui-muted,oklch(0.97 0 0));color:var(--rui-muted-foreground,oklch(0.556 0 0))"

///|
/// Create a stateful alert dialog.
///
/// Unlike [`dialog_content`], [`alert_dialog_content`] does not dismiss on an
/// overlay click and does not render an implicit close icon. A deliberate
/// action or cancel control is therefore always visible in normal use.
pub fn alert_dialog(
  id~ : String,
  default_open? : Bool = false,
  close_on_escape? : Bool = true,
  on_open_change? : @cmd.Emit[Bool],
  on_close? : @cmd.Emit[String],
  children : (AlertDialogScope) -> @html.Html,
) -> @rabbita.Val[@html.Html] {
  dialog(
    id~,
    default_open~,
    modal=true,
    close_on_escape~,
    on_open_change?,
    on_close?,
    scope => children(AlertDialogScope(scope)),
  )
}

///|
pub fn[C : @html.IsChildren] alert_dialog_trigger(
  scope : AlertDialogScope,
  variant? : ButtonVariant = Outline,
  size? : ButtonSize = Default,
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let dialog_scope = scope.0
  let trigger_attrs = dialog_state_attrs(
      attrs,
      "alert-dialog-trigger",
      dialog_scope.open,
    )
    .aria_haspopup("dialog")
    .aria_controls(dialog_scope.id)
    .aria_expanded(ui_bool(dialog_scope.open))
  dialog_control_button(
    slot="alert-dialog-trigger",
    command=dialog_scope.open_command,
    variant~,
    size~,
    disabled~,
    id~,
    class~,
    title~,
    aria_label~,
    on_click~,
    attrs=Some(trigger_attrs),
    style~,
    children,
  )
}

///|
/// Pass native alert-dialog content through without adding a DOM node.
pub fn alert_dialog_portal(children : Array[@html.Html]) -> @html.Html {
  @html.fragment(children)
}

///|
/// Render the non-dismissible backdrop for an alert-dialog scope.
pub fn alert_dialog_overlay(
  scope : AlertDialogScope,
  id? : String,
  class? : String,
  title? : String,
  hidden? : Bool,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  dialog_overlay_part(
    scope.0,
    slot="alert-dialog-overlay",
    close_on_overlay=false,
    id~,
    class~,
    title~,
    hidden~,
    attrs~,
    style~,
  )
}

///|
/// Render a native `role="alertdialog"` with a non-dismissible backdrop.
pub fn[C : @html.IsChildren] alert_dialog_content(
  scope : AlertDialogScope,
  described? : Bool = true,
  aria_label? : String,
  id? : String,
  class? : String,
  title? : String,
  hidden? : Bool,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  overlay_attrs? : @html.Attrs,
  overlay_style? : Array[String] = [],
  native_attrs? : @html.Attrs,
  native_style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let dialog_scope = scope.0
  let native_element_attrs = dialog_state_attrs(
      native_attrs,
      "alert-dialog-native",
      dialog_scope.open,
    )
    .role("alertdialog")
    .aria_modal("true")
    .data_set("modal", "true")
    .data_set("close-on-outside", "false")
    .data_set("close-on-escape", ui_bool(dialog_scope.close_on_escape))
  if aria_label is Some(label) {
    ignore(native_element_attrs.aria_label(label))
  } else {
    ignore(native_element_attrs.aria_labelledby(dialog_scope.title_id()))
  }
  if described {
    ignore(native_element_attrs.aria_describedby(dialog_scope.description_id()))
  }
  let content_attrs = dialog_state_attrs(
    attrs,
    "alert-dialog-content",
    dialog_scope.open,
  )
  let positioner_attrs = dialog_state_attrs(
    None,
    "alert-dialog-positioner",
    dialog_scope.open,
  )
  let content = @html.div(
    style=ui_styles(
      [
        UiBoxSizing,
        DialogContentStyle,
        AlertDialogContentStyle,
        dialog_content_motion_style(dialog_scope.open),
      ],
      style,
    ),
    id?,
    class?,
    title?,
    hidden?,
    attrs=content_attrs,
    children,
  )
  let open = dialog_scope.native_open
  let on_close = dialog_scope.on_close
  let on_cancel = dialog_scope.on_cancel
  let closedby = if dialog_scope.close_on_escape {
    "closerequest"
  } else {
    "none"
  }
  alert_dialog_portal([
    @html.dialog(
      style=ui_styles([UiBoxSizing, DialogNativeStyle], native_style),
      id=dialog_scope.id,
      open?,
      closedby~,
      on_close?,
      on_cancel?,
      attrs=native_element_attrs,
      [
        alert_dialog_overlay(
          scope,
          attrs=ui_attrs(overlay_attrs),
          style=overlay_style,
        ),
        @html.div(
          style=[UiBoxSizing, DialogPositionerStyle],
          attrs=positioner_attrs,
          content,
        ),
      ],
    ),
  ])
}

///|
pub fn[C : @html.IsChildren] alert_dialog_header(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, DialogHeaderStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "alert-dialog-header"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] alert_dialog_footer(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, DialogFooterStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "alert-dialog-footer"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] alert_dialog_title(
  scope : AlertDialogScope,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_id = id.unwrap_or(scope.0.title_id())
  @html.h2(
    style=ui_styles([UiBoxSizing, DialogTitleStyle], style),
    id=element_id,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "alert-dialog-title"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] alert_dialog_description(
  scope : AlertDialogScope,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_id = id.unwrap_or(scope.0.description_id())
  @html.p(
    style=ui_styles([UiBoxSizing, DialogDescriptionStyle], style),
    id=element_id,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "alert-dialog-description"),
    children,
  )
}

///|
/// Render the shadcn alert-dialog media tile for an icon or illustration.
pub fn[C : @html.IsChildren] alert_dialog_media(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, AlertDialogMediaStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "alert-dialog-media"),
    children,
  )
}

///|
/// Render the primary alert-dialog action and close after its caller handler.
pub fn[C : @html.IsChildren] alert_dialog_action(
  scope : AlertDialogScope,
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  dialog_control_button(
    slot="alert-dialog-action",
    command=scope.0.close_command,
    variant=Default,
    size=Default,
    disabled~,
    id~,
    class~,
    title~,
    aria_label~,
    on_click~,
    attrs~,
    style~,
    children,
  )
}

///|
/// Render the secondary alert-dialog cancel control.
pub fn[C : @html.IsChildren] alert_dialog_cancel(
  scope : AlertDialogScope,
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let cancel_attrs = ui_attrs(attrs).autofocus(!disabled)
  dialog_control_button(
    slot="alert-dialog-cancel",
    command=scope.0.close_command,
    variant=Outline,
    size=Default,
    disabled~,
    id~,
    class~,
    title~,
    aria_label~,
    on_click~,
    attrs=Some(cancel_attrs),
    style~,
    children,
  )
}