///|
/// APG Accordion Pattern
/// https://www.w3.org/WAI/ARIA/apg/patterns/accordion/
///
/// An accordion is a vertically stacked set of interactive headings
/// that each contain a title and can reveal or hide associated content.
///
/// Keyboard Interaction:
/// - Enter/Space: Expand/collapse the focused panel
/// - Tab: Move to next focusable element
/// - Down Arrow (optional): Move to next header
/// - Up Arrow (optional): Move to previous header
/// - Home (optional): Move to first header
/// - End (optional): Move to last header
///
/// ARIA:
/// - Heading with button inside (aria-expanded, aria-controls)
/// - Panel with role="region" and aria-labelledby (optional)

///|
/// Accordion item data
pub(all) struct AccordionItem[E] {
  id : String // Unique item ID
  heading : String // Header text
  level : Int // Heading level (1-6)
  content : Array[@luna.Node[E, String]] // Panel content
}

///|
/// Create an accordion item
pub fn[E] accordion_item(
  id : String,
  heading : String,
  level? : Int,
  content : Array[@luna.Node[E, String]],
) -> AccordionItem[E] {
  { id, heading, level: level.unwrap_or(3), content }
}

///|
/// Create an accordion header button.
///
/// Parameters:
/// - id: Button ID
/// - expanded: Whether the panel is expanded
/// - controls: ID of the controlled panel
/// - level: Heading level (1-6)
/// - on_click: Click handler to toggle
/// - on_keydown: Keydown handler (for arrow key navigation)
/// - children: Header content
pub fn[E] accordion_header(
  id : String,
  expanded : Bool,
  controls : String,
  level? : Int,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let heading_tag = "h" + level.unwrap_or(3).to_string()
  let button_attrs : Array[(String, @luna.Attr[E, String])] = [
    ("type", @luna.attr_static("button")),
    ("id", @luna.attr_static(id)),
    (
      "aria-expanded",
      @luna.attr_static(if expanded { "true" } else { "false" }),
    ),
    ("aria-controls", @luna.attr_static(controls)),
  ]
  if on_click is Some(handler) {
    button_attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    button_attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  let button = @luna.h("button", button_attrs) <| children
  @luna.h(heading_tag, []) <| [
    button,
  ]
}

///|
/// Create a dynamic accordion header with signal-based expanded state.
pub fn[E] accordion_header_dyn(
  id : String,
  expanded : @resource.Signal[Bool],
  controls : String,
  level? : Int,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let heading_tag = "h" + level.unwrap_or(3).to_string()
  let button_attrs : Array[(String, @luna.Attr[E, String])] = [
    ("type", @luna.attr_static("button")),
    ("id", @luna.attr_static(id)),
    (
      "aria-expanded",
      @luna.attr_dynamic(fn() { if expanded.get() { "true" } else { "false" } }),
    ),
    ("aria-controls", @luna.attr_static(controls)),
  ]
  if on_click is Some(handler) {
    button_attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    button_attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  let button = @luna.h("button", button_attrs) <| children
  @luna.h(heading_tag, []) <| [
    button,
  ]
}

///|
/// Create a dynamic accordion header with computed expanded state (accepts getter function).
/// Useful for derived accordion states.
pub fn[E] accordion_header_computed(
  id : String,
  expanded : () -> Bool,
  controls : String,
  level? : Int,
  on_click? : @luna.EventHandler[E],
  on_keydown? : @luna.EventHandler[E],
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let heading_tag = "h" + level.unwrap_or(3).to_string()
  let button_attrs : Array[(String, @luna.Attr[E, String])] = [
    ("type", @luna.attr_static("button")),
    ("id", @luna.attr_static(id)),
    ("aria-expanded", @luna.attr_dynamic(fn() { bool_to_aria(expanded()) })),
    ("aria-controls", @luna.attr_static(controls)),
  ]
  if on_click is Some(handler) {
    button_attrs.push(("click", @luna.attr_handler(handler)))
  }
  if on_keydown is Some(handler) {
    button_attrs.push(("keydown", @luna.attr_handler(handler)))
  }
  let button = @luna.h("button", button_attrs) <| children
  @luna.h(heading_tag, []) <| [
    button,
  ]
}

///|
/// Create an accordion panel.
///
/// Parameters:
/// - id: Panel ID (referenced by header's aria-controls)
/// - labelledby: ID of the header button
/// - hidden: Whether the panel is hidden
/// - use_region: Whether to use role="region" (recommended for <= 6 panels)
/// - children: Panel content
pub fn[E] accordion_panel(
  id : String,
  labelledby : String,
  hidden : Bool,
  use_region? : Bool,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
  ]
  if use_region is Some(true) {
    attrs.push(("role", @luna.attr_static("region")))
  }
  if hidden {
    attrs.push(("hidden", @luna.attr_static("")))
  }
  @luna.h("div", attrs) <| children
}

///|
/// Create a dynamic accordion panel with signal-based visibility.
pub fn[E] accordion_panel_dyn(
  id : String,
  labelledby : String,
  visible : @resource.Signal[Bool],
  use_region? : Bool,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
  ]
  if use_region is Some(true) {
    attrs.push(("role", @luna.attr_static("region")))
  }
  @luna.show(fn() { visible.get() }, fn() { @luna.h("div", attrs) <| children })
}

///|
/// Create a accordion panel with computed visibility (accepts getter function).
pub fn[E] accordion_panel_computed(
  id : String,
  labelledby : String,
  visible : () -> Bool,
  use_region? : Bool,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("id", @luna.attr_static(id)),
    ("aria-labelledby", @luna.attr_static(labelledby)),
  ]
  if use_region is Some(true) {
    attrs.push(("role", @luna.attr_static("region")))
  }
  @luna.show(visible, fn() { @luna.h("div", attrs) <| children })
}

///|
/// Build a complete accordion from items.
/// Only one panel can be expanded at a time (single-select mode).
///
/// Parameters:
/// - items: Array of AccordionItem
/// - expanded_index: Index of currently expanded item (-1 for all collapsed)
/// - use_regions: Whether to add role="region" to panels
pub fn[E] accordion(
  items : Array[AccordionItem[E]],
  expanded_index : Int,
  use_regions? : Bool,
) -> @luna.Node[E, String] {
  let use_reg = use_regions.unwrap_or(items.length() <= 6)
  let children : Array[@luna.Node[E, String]] = []
  for i, item in items {
    let is_expanded = i == expanded_index
    let header_id = "accordion-header-" + item.id
    let panel_id = "accordion-panel-" + item.id
    children.push(
      accordion_header(header_id, is_expanded, panel_id, level=item.level) <| [
        @luna.text(item.heading),
      ],
    )
    children.push(
      accordion_panel(panel_id, header_id, !is_expanded, use_region=use_reg) <| item.content,
    )
  }
  @luna.h("div", [("data-accordion", @luna.attr_static(""))]) <| children
}

///|
/// Build a multi-select accordion where multiple panels can be open.
///
/// Parameters:
/// - items: Array of AccordionItem
/// - expanded_ids: Array of expanded item IDs
/// - use_regions: Whether to add role="region" to panels
pub fn[E] accordion_multi(
  items : Array[AccordionItem[E]],
  expanded_ids : Array[String],
  use_regions? : Bool,
) -> @luna.Node[E, String] {
  let use_reg = use_regions.unwrap_or(items.length() <= 6)
  let children : Array[@luna.Node[E, String]] = []
  for item in items {
    let is_expanded = expanded_ids.contains(item.id)
    let header_id = "accordion-header-" + item.id
    let panel_id = "accordion-panel-" + item.id
    children.push(
      accordion_header(header_id, is_expanded, panel_id, level=item.level) <| [
        @luna.text(item.heading),
      ],
    )
    children.push(
      accordion_panel(panel_id, header_id, !is_expanded, use_region=use_reg) <| item.content,
    )
  }
  @luna.h("div", [("data-accordion", @luna.attr_static("multi"))]) <| children
}

///|
/// Build a dynamic accordion with signal-based expanded index.
/// This is the recommended way to create accordions with reactive state.
///
/// Parameters:
/// - items: Array of AccordionItem
/// - expanded_index: Signal containing the currently expanded item index (-1 for all collapsed)
/// - use_regions: Whether to add role="region" to panels
pub fn[E] accordion_dyn(
  items : Array[AccordionItem[E]],
  expanded_index : @resource.Signal[Int],
  use_regions? : Bool,
) -> @luna.Node[E, String] {
  let use_reg = use_regions.unwrap_or(items.length() <= 6)
  let children : Array[@luna.Node[E, String]] = []
  for i, item in items {
    let index = i // capture index
    let header_id = "accordion-header-" + item.id
    let panel_id = "accordion-panel-" + item.id
    children.push(
      accordion_header_computed(
        header_id,
        fn() { expanded_index.get() == index },
        panel_id,
        level=item.level,
        on_click=@luna.handler(fn(_) {
          if expanded_index.get() == index {
            expanded_index.set(-1)
          } else {
            expanded_index.set(index)
          }
        }),
      ) <| [
        @luna.text(item.heading),
      ],
    )
    children.push(
      accordion_panel_computed(
        panel_id,
        header_id,
        fn() { expanded_index.get() == index },
        use_region=use_reg,
      ) <| item.content,
    )
  }
  @luna.h("div", [("data-accordion", @luna.attr_static(""))]) <| children
}

// =============================================================================
// Keyboard Navigation for Accordion (APG optional but recommended)
// =============================================================================

///|
/// Focus an accordion header by ID.
fn focus_accordion_header(header_id : String) -> Unit {
  let doc = @js.global_this()._get("document")
  let el = doc._call("getElementById", [@js.any(header_id)])
  let _ = el._call("focus", [])
}

///|
/// Create a keyboard handler for accordion header navigation.
///
/// APG keyboard support:
/// - ArrowDown: Move focus to next header
/// - ArrowUp: Move focus to previous header
/// - Home: Move focus to first header
/// - End: Move focus to last header
/// - Enter/Space: Toggle panel (handled by browser for button)
///
/// Parameters:
/// - header_ids: Array of header button IDs
/// - current_index: Index of the current header
/// - on_toggle: Optional callback when Enter/Space is pressed
pub fn make_accordion_keydown_handler(
  header_ids : Array[String],
  current_index : Int,
  on_toggle? : () -> Unit,
) -> @luna.EventHandler[@js.Any] {
  @luna.handler(fn(e : @js.Any) {
    let key : String = e._get("key").cast()
    let count = header_ids.length()
    if count == 0 {
      return
    }
    match key {
      "ArrowDown" => {
        let _ = e._call("preventDefault", [])
        let next_index = if current_index < count - 1 {
          current_index + 1
        } else {
          current_index // Don't wrap
        }
        focus_accordion_header(header_ids[next_index])
      }
      "ArrowUp" => {
        let _ = e._call("preventDefault", [])
        let prev_index = if current_index > 0 {
          current_index - 1
        } else {
          current_index // Don't wrap
        }
        focus_accordion_header(header_ids[prev_index])
      }
      "Home" => {
        let _ = e._call("preventDefault", [])
        focus_accordion_header(header_ids[0])
      }
      "End" => {
        let _ = e._call("preventDefault", [])
        focus_accordion_header(header_ids[count - 1])
      }
      " " | "Enter" => if on_toggle is Some(toggle) { toggle() }
      _ => ()
    }
  })
}

///|
/// Create a complete interactive accordion with keyboard navigation.
///
/// Features:
/// - Arrow key navigation between headers
/// - Home/End navigation
/// - Enter/Space toggles panel
/// - Single-select mode (one panel open at a time)
///
/// Parameters:
/// - id: Accordion container ID
/// - items: Array of AccordionItem
/// - expanded_index: Signal for currently expanded item (-1 for all collapsed)
/// - use_regions: Whether to use role="region" on panels
pub fn accordion_interactive(
  id : String,
  items : Array[AccordionItem[@js.Any]],
  expanded_index : @resource.Signal[Int],
  use_regions? : Bool,
) -> @luna.Node[@js.Any, String] {
  let use_reg = use_regions.unwrap_or(items.length() <= 6)
  // Build header IDs array
  let header_ids : Array[String] = []
  for item in items {
    header_ids.push("accordion-header-" + item.id)
  }
  let children : Array[@luna.Node[@js.Any, String]] = []
  let mut idx = 0
  while idx < items.length() {
    let item = items[idx]
    let index = idx
    let header_id = "accordion-header-" + item.id
    let panel_id = "accordion-panel-" + item.id
    let toggle = fn() {
      if expanded_index.get() == index {
        expanded_index.set(-1)
      } else {
        expanded_index.set(index)
      }
    }
    let keydown_handler = make_accordion_keydown_handler(
      header_ids,
      index,
      on_toggle=toggle,
    )
    let click_handler = @luna.handler(fn(_) { toggle() })
    // Build header
    let heading_tag = "h" + item.level.to_string()
    let button_attrs : Array[(String, @luna.Attr[@js.Any, String])] = [
      ("type", @luna.attr_static("button")),
      ("id", @luna.attr_static(header_id)),
      (
        "aria-expanded",
        @luna.attr_dynamic(fn() {
          if expanded_index.get() == index {
            "true"
          } else {
            "false"
          }
        }),
      ),
      ("aria-controls", @luna.attr_static(panel_id)),
      ("click", @luna.attr_handler(click_handler)),
      ("keydown", @luna.attr_handler(keydown_handler)),
    ]
    let button = @luna.h("button", button_attrs) <| [
        @luna.text(item.heading),
      ]
    let header = @luna.h(heading_tag, []) <| [
        button,
      ]
    children.push(header)
    // Build panel
    let panel_attrs : Array[(String, @luna.Attr[@js.Any, String])] = [
      ("id", @luna.attr_static(panel_id)),
      ("aria-labelledby", @luna.attr_static(header_id)),
    ]
    if use_reg {
      panel_attrs.push(("role", @luna.attr_static("region")))
    }
    children.push(
      @luna.show(fn() { expanded_index.get() == index }, fn() {
        @luna.h("div", panel_attrs) <| item.content
      }),
    )
    idx = idx + 1
  }
  @luna.h(
    "div",
    [("id", @luna.attr_static(id)), ("data-accordion", @luna.attr_static(""))],
  ) <| children
}