///|
pub(all) struct ComboboxState {
  values : Array[String]
  query : String
  open : Bool
} derive(Eq, Debug)

///|
pub fn combobox_state(
  values? : Array[String] = [],
  query? : String = "",
  open? : Bool = false,
) -> ComboboxState {
  { values, query, open, }
}

///|
struct ComboboxScope {
  id : String
  state : ComboboxState
  options : Array[ChoiceOption]
  filtered_options : Array[ChoiceOption]
  disabled : Bool
  invalid : Bool
  required : Bool
  visible : Bool
  bounds : @headless.Bounds?
  update : @minimoon.Emit[ComboboxIntent]
  show : @minimoon.Cmd
}

///|
pub fn ComboboxScope::state(self : ComboboxScope) -> ComboboxState {
  self.state
}

///|
pub fn ComboboxScope::options(
  self : ComboboxScope,
  filtered? : Bool = true,
) -> Array[ChoiceOption] {
  if filtered {
    self.filtered_options
  } else {
    self.options
  }
}

///|
pub fn ComboboxScope::close(self : ComboboxScope) -> @minimoon.Cmd {
  (self.update)(SetOpen(false))
}

///|
pub fn ComboboxScope::open(self : ComboboxScope) -> @minimoon.Cmd {
  self.show
}

///|
pub fn ComboboxScope::choose(
  self : ComboboxScope,
  value : String,
) -> @minimoon.Cmd {
  (self.update)(Choose(value))
}

///|
pub fn ComboboxScope::remove(
  self : ComboboxScope,
  value : String,
) -> @minimoon.Cmd {
  (self.update)(Remove(value))
}

///|
pub fn combobox_input(
  scope : ComboboxScope,
  placeholder? : String = "Search",
) -> @minimoon.Node {
  @minimoon.input(
    id=scope.id + "-input",
    event_key=scope.id + "/query",
    value=scope.state.query,
    disabled=scope.disabled,
    on_input=@minimoon.Emit(query => {
      if scope.disabled {
        @minimoon.none
      } else {
        (scope.update)(SetQuery(query))
      }
    }),
    placeholder~,
    semantics=@minimoon.semantics(
      role=@minimoon.ComboboxRole,
      invalid=scope.invalid,
      required=scope.required,
      expanded=scope.state.open,
    ),
  )
}

///|
pub fn combobox_chips_input(
  scope : ComboboxScope,
  placeholder? : String = "Search",
) -> @minimoon.Node {
  combobox_input(scope, placeholder~)
}

///|
pub fn combobox_trigger(
  scope : ComboboxScope,
  label? : String = "Choose",
) -> @minimoon.Node {
  @minimoon.button(
    id=scope.id + "-trigger",
    event_key=scope.id + "/open",
    on_tap=scope.show,
    disabled=scope.disabled,
    semantics=@minimoon.semantics(
      expanded=scope.state.open,
      has_popup="listbox",
    ),
    label,
  )
}

///|
pub fn combobox_clear(
  scope : ComboboxScope,
  label? : String = "Clear",
) -> @minimoon.Node {
  @minimoon.button(
    event_key=scope.id + "/clear",
    disabled=scope.disabled,
    on_tap=if scope.disabled { @minimoon.none } else { (scope.update)(Clear) },
    label,
  )
}

///|
pub fn[C : @minimoon.IsChildren] combobox_chip(
  scope : ComboboxScope,
  value~ : String,
  children : C,
  removable? : Bool = true,
) -> @minimoon.Node {
  ui_container("combobox-chip", [
    @minimoon.fragment(children.to_nodes()),
    if removable {
      @minimoon.button(
        event_key=scope.id + "/remove/" + value,
        disabled=scope.disabled,
        on_tap=scope.remove(value),
        "Remove",
      )
    } else {
      @minimoon.nothing()
    },
  ])
}

///|
pub fn[C : @minimoon.IsChildren] combobox_item(
  scope : ComboboxScope,
  value~ : String,
  children : C,
  disabled? : Bool = false,
) -> @minimoon.Node {
  @minimoon.button_children(
    event_key=scope.id + "/option/" + value,
    disabled=disabled || scope.disabled,
    on_tap=if disabled { @minimoon.none } else { scope.choose(value) },
    semantics=@minimoon.semantics(
      role=@minimoon.OptionRole,
      selected=scope.state.values.contains(value),
    ),
    children,
  )
}

///|
pub fn combobox_value(
  scope : ComboboxScope,
  placeholder? : String = "",
) -> @minimoon.Node {
  let labels = scope.state.values.map(key => {
    select_value(scope.options, key, placeholder=key)
  })
  @minimoon.text(
    if labels.is_empty() {
      placeholder
    } else {
      labels.join(", ")
    },
  )
}

///|
pub fn combobox_option(
  value~ : String,
  label~ : String,
  disabled? : Bool = false,
) -> ChoiceOption {
  choice_option(key=value, label~, disabled~)
}