///|
/// APG Tabs Pattern
/// https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
///
/// Tabs are a set of layered sections of content that display one panel at a time.
/// Each tab panel has an associated tab element that controls its visibility.
///
/// Keyboard Interaction:
/// - Tab: Move focus into/out of tab list
/// - Left/Right Arrow: Move between tabs (horizontal)
/// - Up/Down Arrow: Move between tabs (vertical)
/// - Space/Enter: Activate tab (manual activation mode)
/// - Home: Move to first tab (optional)
/// - End: Move to last tab (optional)
///
/// ARIA:
/// - tablist: Container for tabs
/// - tab: Each tab button with aria-selected, aria-controls
/// - tabpanel: Content panel with aria-labelledby

///|
/// Tabs orientation
pub(all) enum TabsOrientation {
  Horizontal // Default, use Left/Right arrows
  Vertical // Use Up/Down arrows
}

///|
/// Tab item data for building tabs
pub(all) struct TabItem[E] {
  id : String // Unique tab ID
  label : String // Tab label text
  panel : Array[@luna.Node[E, String]] // Panel content
}

///|
/// Create a tab item
pub fn[E] tab_item(
  id : String,
  label : String,
  panel : Array[@luna.Node[E, String]],
) -> TabItem[E] {
  { id, label, panel }
}

///|
/// Create a complete tablist container.
///
/// Parameters:
/// - aria_label: Accessible label for the tab list
/// - orientation: Horizontal or Vertical (default: Horizontal)
/// - children: Tab buttons (use `tab()` to create)
pub fn[E] tablist(
  aria_label? : String,
  orientation? : TabsOrientation,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tablist")),
  ]
  if aria_label is Some(label) {
    attrs.push(("aria-label", @luna.attr_static(label)))
  }
  if orientation is Some(Vertical) {
    attrs.push(("aria-orientation", @luna.attr_static("vertical")))
  }
  @luna.h("div", attrs) <| children
}

///|
/// Create a single tab button.
///
/// Parameters:
/// - id: Tab ID (used for aria-controls on the tab and id on the panel)
/// - selected: Whether this tab is currently selected
/// - controls: ID of the associated tabpanel
/// - on_click: Click handler to activate the tab
/// - on_keydown: Keydown handler (for arrow key navigation)
/// - children: Tab label content
pub fn[E] tab(
  id : String,
  selected : Bool,
  controls : String,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tab")),
    ("id", @luna.attr_static(id)),
    (
      "aria-selected",
      @luna.attr_static(if selected { "true" } else { "false" }),
    ),
    ("aria-controls", @luna.attr_static(controls)),
    ("tabindex", @luna.attr_static(if selected { "0" } else { "-1" })),
  ]
  if on_click is Some(handler) {
    attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  @luna.h("button", attrs) <| children
}

///|
/// Create a dynamic tab with signal-based selected state.
pub fn[E] tab_dyn(
  id : String,
  selected : @resource.Signal[Bool],
  controls : String,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tab")),
    ("id", @luna.attr_static(id)),
    (
      "aria-selected",
      @luna.attr_dynamic(fn() { if selected.get() { "true" } else { "false" } }),
    ),
    ("aria-controls", @luna.attr_static(controls)),
    (
      "tabindex",
      @luna.attr_dynamic(fn() { if selected.get() { "0" } else { "-1" } }),
    ),
  ]
  if on_click is Some(handler) {
    attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  @luna.h("button", attrs) <| children
}

///|
/// Create a tabpanel.
///
/// Parameters:
/// - id: Panel ID (referenced by tab's aria-controls)
/// - labelledby: ID of the associated tab (for aria-labelledby)
/// - hidden: Whether the panel is hidden
/// - children: Panel content
pub fn[E] tabpanel(
  id : String,
  labelledby : String,
  hidden : Bool,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tabpanel")),
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
    ("tabindex", @luna.attr_static("0")),
  ]
  if hidden {
    attrs.push(("hidden", @luna.attr_static("")))
  }
  @luna.h("div", attrs) <| children
}

///|
/// Create a dynamic tabpanel with signal-based visibility.
pub fn[E] tabpanel_dyn(
  id : String,
  labelledby : String,
  visible : @resource.Signal[Bool],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tabpanel")),
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
    ("tabindex", @luna.attr_static("0")),
  ]
  @luna.show(fn() { visible.get() }, fn() { @luna.h("div", attrs) <| children })
}

///|
/// Create a tab with computed selected state (accepts getter function).
/// This is useful when the selected state is derived from another signal.
/// Example: `tab_computed(id, fn() { active_tab.get() == 0 }, controls, children)`
pub fn[E] tab_computed(
  id : String,
  selected : () -> Bool,
  controls : String,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tab")),
    ("id", @luna.attr_static(id)),
    ("aria-selected", @luna.attr_dynamic(fn() { bool_to_aria(selected()) })),
    ("aria-controls", @luna.attr_static(controls)),
    (
      "tabindex",
      @luna.attr_dynamic(fn() { if selected() { "0" } else { "-1" } }),
    ),
  ]
  if on_click is Some(handler) {
    attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  @luna.h("button", attrs) <| children
}

///|
/// Create a tabpanel with computed visibility (accepts getter function).
/// Uses style display instead of hidden attribute for proper visibility control.
pub fn[E] tabpanel_computed(
  id : String,
  labelledby : String,
  visible : () -> Bool,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("tabpanel")),
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
    ("tabindex", @luna.attr_static("0")),
    (
      "style",
      @luna.attr_dynamic(fn() { if visible() { "" } else { "display:none" } }),
    ),
  ]
  @luna.h("div", attrs) <| children
}

///|
/// Build a complete tabs component from tab items.
/// This is a convenience function that creates the full structure.
///
/// Parameters:
/// - items: Array of TabItem containing tab data
/// - selected_index: Index of the currently selected tab
/// - aria_label: Accessible label for the tabs
/// - orientation: Tabs orientation
pub fn[E] tabs(
  items : Array[TabItem[E]],
  selected_index : Int,
  aria_label? : String,
  orientation? : TabsOrientation,
) -> @luna.Node[E, String] {
  let tab_buttons : Array[@luna.Node[E, String]] = []
  let panels : Array[@luna.Node[E, String]] = []
  for i, item in items {
    let is_selected = i == selected_index
    let tab_id = "tab-" + item.id
    let panel_id = "panel-" + item.id
    tab_buttons.push(
      tab(tab_id, is_selected, panel_id) <| [
        @luna.text(item.label),
      ],
    )
    panels.push(tabpanel(panel_id, tab_id, !is_selected) <| item.panel)
  }
  let list = tablist(aria_label?, orientation?) <| tab_buttons
  let children : Array[@luna.Node[E, String]] = [list]
  for p in panels {
    children.push(p)
  }
  @luna.fragment <| children
}

///|
/// Build a dynamic tabs component with signal-based selection.
/// This is the recommended way to create tabs with reactive state.
///
/// Parameters:
/// - items: Array of TabItem containing tab data
/// - selected_index: Signal containing the currently selected tab index
/// - aria_label: Accessible label for the tabs
/// - orientation: Tabs orientation
pub fn[E] tabs_dyn(
  items : Array[TabItem[E]],
  selected_index : @resource.Signal[Int],
  aria_label? : String,
  orientation? : TabsOrientation,
) -> @luna.Node[E, String] {
  let tab_buttons : Array[@luna.Node[E, String]] = []
  let panels : Array[@luna.Node[E, String]] = []
  for i, item in items {
    let index = i // capture index
    let tab_id = "tab-" + item.id
    let panel_id = "panel-" + item.id
    tab_buttons.push(
      tab_computed(
        tab_id,
        fn() { selected_index.get() == index },
        panel_id,
        on_click=@luna.handler(fn(_) { selected_index.set(index) }),
      ) <| [
        @luna.text(item.label),
      ],
    )
    panels.push(
      tabpanel_computed(
        panel_id,
        tab_id,
        fn() { selected_index.get() == index },
      ) <| item.panel,
    )
  }
  let list = tablist(aria_label?, orientation?) <| tab_buttons
  let children : Array[@luna.Node[E, String]] = [list]
  for p in panels {
    children.push(p)
  }
  @luna.fragment <| children
}

///|
/// Build a dynamic tabs component with full keyboard support.
/// Includes arrow key navigation, Home/End support.
///
/// Parameters:
/// - items: Array of TabItem containing tab data
/// - selected_index: Signal containing the currently selected tab index
/// - aria_label: Accessible label for the tabs
/// - orientation: Tabs orientation (affects which arrow keys to use)
pub fn tabs_interactive(
  items : Array[TabItem[@js.Any]],
  selected_index : @resource.Signal[Int],
  aria_label? : String,
  orientation? : TabsOrientation,
) -> @luna.Node[@js.Any, String] {
  let is_vertical = match orientation {
    Some(Vertical) => true
    _ => false
  }
  let length = items.length()
  let keydown_handler = if is_vertical {
    make_vertical_nav_handler(fn() { selected_index.get() }, length, fn(i) {
      selected_index.set(i)
    })
  } else {
    make_horizontal_nav_handler(fn() { selected_index.get() }, length, fn(i) {
      selected_index.set(i)
    })
  }
  let tab_buttons : Array[@luna.Node[@js.Any, String]] = []
  let panels : Array[@luna.Node[@js.Any, String]] = []
  for i, item in items {
    let index = i // capture index
    let tab_id = "tab-" + item.id
    let panel_id = "panel-" + item.id
    tab_buttons.push(
      tab_computed(
        tab_id,
        fn() { selected_index.get() == index },
        panel_id,
        on_click=@luna.handler(fn(_) { selected_index.set(index) }),
        on_keydown=keydown_handler,
      ) <| [
        @luna.text(item.label),
      ],
    )
    panels.push(
      tabpanel_computed(
        panel_id,
        tab_id,
        fn() { selected_index.get() == index },
      ) <| item.panel,
    )
  }
  let list = tablist(aria_label?, orientation?) <| tab_buttons
  let children : Array[@luna.Node[@js.Any, String]] = [list]
  for p in panels {
    children.push(p)
  }
  @luna.fragment <| children
}