///|
/// APG Radio Group Pattern
/// https://www.w3.org/WAI/ARIA/apg/patterns/radio/
///
/// A radio group is a set of checkable buttons where only one can be checked at a time.
///
/// Keyboard Interaction:
/// - Tab/Shift+Tab: Move focus into/out of the group
/// - Space: Check focused radio button
/// - Right/Down Arrow: Move to next radio, check it
/// - Left/Up Arrow: Move to previous radio, check it
///
/// ARIA:
/// - radiogroup: Container role
/// - radio: Each button with aria-checked

///|
/// Radio option data
pub(all) struct RadioOption[E] {
  value : String // Option value
  label : Array[@luna.Node[E, String]] // Label content
  disabled : Bool // Whether disabled
}

///|
/// Create a radio option
pub fn[E] radio_option(
  value : String,
  label : Array[@luna.Node[E, String]],
  disabled? : Bool,
) -> RadioOption[E] {
  { value, label, disabled: disabled.unwrap_or(false) }
}

///|
/// Create a radio group container.
///
/// Parameters:
/// - aria_label: Accessible label for the group
/// - aria_labelledby: ID of labelling element
/// - children: Radio buttons (use `radio()` to create)
pub fn[E] radiogroup(
  aria_label? : String,
  aria_labelledby? : String,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("radiogroup")),
  ]
  if aria_label is Some(label) {
    attrs.push(("aria-label", @luna.attr_static(label)))
  }
  if aria_labelledby is Some(id) {
    attrs.push(("aria-labelledby", @luna.attr_static(id)))
  }
  @luna.h("div", attrs, children)
}

///|
/// Create a single radio button.
///
/// Parameters:
/// - value: Radio value
/// - checked: Whether this radio is checked
/// - disabled: Whether this radio is disabled
/// - on_click: Click handler
/// - on_keydown: Keydown handler (for arrow key navigation)
/// - children: Radio label content
pub fn[E] radio(
  value : String,
  checked : Bool,
  disabled? : Bool,
  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("radio")),
    ("aria-checked", @luna.attr_static(if checked { "true" } else { "false" })),
    ("tabindex", @luna.attr_static(if checked { "0" } else { "-1" })),
    ("data-value", @luna.attr_static(value)),
  ]
  if disabled is Some(true) {
    attrs.push(("aria-disabled", @luna.attr_static("true")))
  }
  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("div", attrs, children)
}

///|
/// Create a dynamic radio with signal-based checked state.
pub fn[E] radio_dyn(
  value : String,
  selected_value : @resource.Signal[String],
  disabled? : Bool,
  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("radio")),
    (
      "aria-checked",
      @luna.attr_dynamic(fn() {
        if selected_value.get() == value {
          "true"
        } else {
          "false"
        }
      }),
    ),
    (
      "tabindex",
      @luna.attr_dynamic(fn() {
        if selected_value.get() == value {
          "0"
        } else {
          "-1"
        }
      }),
    ),
    ("data-value", @luna.attr_static(value)),
  ]
  if disabled is Some(true) {
    attrs.push(("aria-disabled", @luna.attr_static("true")))
  }
  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("div", attrs, children)
}

///|
/// Create a radio with computed checked state (accepts getter function).
/// Useful for derived radio states.
pub fn[E] radio_computed(
  value : String,
  checked : () -> Bool,
  disabled? : Bool,
  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("radio")),
    ("aria-checked", @luna.attr_dynamic(fn() { bool_to_aria(checked()) })),
    (
      "tabindex",
      @luna.attr_dynamic(fn() { if checked() { "0" } else { "-1" } }),
    ),
    ("data-value", @luna.attr_static(value)),
  ]
  if disabled is Some(true) {
    attrs.push(("aria-disabled", @luna.attr_static("true")))
  }
  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("div", attrs, children)
}

///|
/// Build a complete radio group from options.
///
/// Parameters:
/// - name: Group name (for form submission)
/// - options: Array of RadioOption
/// - selected_value: Currently selected value
/// - aria_label: Accessible label for the group
pub fn[E] radio_group(
  name : String,
  options : Array[RadioOption[E]],
  selected_value : String,
  aria_label? : String,
) -> @luna.Node[E, String] {
  let radios : Array[@luna.Node[E, String]] = []
  for option in options {
    let is_checked = option.value == selected_value
    radios.push(
      radio(option.value, is_checked, disabled=option.disabled, option.label),
    )
  }
  let attrs : Array[(String, @luna.Attr[E, String])] = [
    ("role", @luna.attr_static("radiogroup")),
    ("data-name", @luna.attr_static(name)),
  ]
  if aria_label is Some(label) {
    attrs.push(("aria-label", @luna.attr_static(label)))
  }
  @luna.h("div", attrs, radios)
}

///|
/// Create a native radio input with label.
/// Uses HTML native radio for better form integration.
pub fn[E] radio_native(
  name : String,
  value : String,
  checked : Bool,
  disabled? : Bool,
  id? : String,
  children : Array[@luna.Node[E, String]],
) -> @luna.Node[E, String] {
  let input_id = id.unwrap_or(name + "-" + value)
  let input_attrs : Array[(String, @luna.Attr[E, String])] = [
    ("type", @luna.attr_static("radio")),
    ("name", @luna.attr_static(name)),
    ("value", @luna.attr_static(value)),
    ("id", @luna.attr_static(input_id)),
  ]
  if checked {
    input_attrs.push(("checked", @luna.attr_static("")))
  }
  if disabled is Some(true) {
    input_attrs.push(("disabled", @luna.attr_static("")))
  }
  let label_attrs : Array[(String, @luna.Attr[E, String])] = [
    ("for", @luna.attr_static(input_id)),
  ]
  @luna.fragment([
    @luna.h("input", input_attrs, []),
    @luna.h("label", label_attrs, children),
  ])
}

// =============================================================================
// Keyboard Navigation for Radio Group (APG required)
// =============================================================================

///|
/// Focus a radio element by ID.
fn focus_radio(radio_id : String) -> Unit {
  let doc = @js.global_this()._get("document")
  let el = doc._call("getElementById", [@js.any(radio_id)])
  let _ = el._call("focus", [])
}

///|
/// Find next enabled index (wrapping).
fn find_next_enabled(
  options : Array[RadioOption[@js.Any]],
  current_index : Int,
) -> Int {
  let count = options.length()
  let mut idx = (current_index + 1) % count
  let mut iterations = 0
  while iterations < count {
    if !options[idx].disabled {
      return idx
    }
    idx = (idx + 1) % count
    iterations = iterations + 1
  }
  current_index // All disabled, stay
}

///|
/// Find previous enabled index (wrapping).
fn find_prev_enabled(
  options : Array[RadioOption[@js.Any]],
  current_index : Int,
) -> Int {
  let count = options.length()
  let mut idx = if current_index == 0 { count - 1 } else { current_index - 1 }
  let mut iterations = 0
  while iterations < count {
    if !options[idx].disabled {
      return idx
    }
    idx = if idx == 0 { count - 1 } else { idx - 1 }
    iterations = iterations + 1
  }
  current_index // All disabled, stay
}

///|
/// Find first enabled index.
fn find_first_enabled(options : Array[RadioOption[@js.Any]]) -> Int {
  let mut idx = 0
  while idx < options.length() {
    if !options[idx].disabled {
      return idx
    }
    idx = idx + 1
  }
  0
}

///|
/// Find last enabled index.
fn find_last_enabled(options : Array[RadioOption[@js.Any]]) -> Int {
  let mut idx = options.length() - 1
  while idx >= 0 {
    if !options[idx].disabled {
      return idx
    }
    idx = idx - 1
  }
  options.length() - 1
}

///|
/// Create a keyboard handler for radio group with arrow navigation.
///
/// APG keyboard support:
/// - ArrowDown/ArrowRight: Move to next and select (wraps)
/// - ArrowUp/ArrowLeft: Move to previous and select (wraps)
/// - Home: Move to first and select
/// - End: Move to last and select
/// - Space: Select focused radio
///
/// Parameters:
/// - options: Array of radio options
/// - radio_ids: Array of radio element IDs
/// - current_index: Index of the current radio
/// - on_select: Callback to select a value by index
pub fn make_radio_keydown_handler(
  options : Array[RadioOption[@js.Any]],
  radio_ids : Array[String],
  current_index : Int,
  on_select : (Int) -> Unit,
) -> @luna.EventHandler[@js.Any] {
  @luna.handler(fn(e : @js.Any) {
    let key : String = e._get("key").cast()
    match key {
      "ArrowDown" | "ArrowRight" => {
        let _ = e._call("preventDefault", [])
        let next_idx = find_next_enabled(options, current_index)
        on_select(next_idx)
        focus_radio(radio_ids[next_idx])
      }
      "ArrowUp" | "ArrowLeft" => {
        let _ = e._call("preventDefault", [])
        let prev_idx = find_prev_enabled(options, current_index)
        on_select(prev_idx)
        focus_radio(radio_ids[prev_idx])
      }
      "Home" => {
        let _ = e._call("preventDefault", [])
        let first_idx = find_first_enabled(options)
        on_select(first_idx)
        focus_radio(radio_ids[first_idx])
      }
      "End" => {
        let _ = e._call("preventDefault", [])
        let last_idx = find_last_enabled(options)
        on_select(last_idx)
        focus_radio(radio_ids[last_idx])
      }
      " " => {
        let _ = e._call("preventDefault", [])
        if !options[current_index].disabled {
          on_select(current_index)
        }
      }
      _ => ()
    }
  })
}

///|
/// Create a complete interactive radio group with keyboard navigation.
///
/// Features:
/// - Arrow key navigation (wraps)
/// - Home/End navigation
/// - Space selects
/// - Roving tabindex
///
/// Parameters:
/// - id: Group container ID
/// - name: Form field name
/// - options: Array of RadioOption
/// - selected: Signal for currently selected value
/// - aria_label: Accessible label
pub fn radiogroup_interactive(
  id : String,
  name : String,
  options : Array[RadioOption[@js.Any]],
  selected : @resource.Signal[String],
  aria_label? : String,
) -> @luna.Node[@js.Any, String] {
  // Build radio IDs
  let radio_ids : Array[String] = []
  let mut i = 0
  while i < options.length() {
    radio_ids.push(id + "-" + options[i].value)
    i = i + 1
  }
  // Find current index from selected value
  fn find_selected_index() -> Int {
    let sel = selected.get()
    let mut idx = 0
    while idx < options.length() {
      if options[idx].value == sel {
        return idx
      }
      idx = idx + 1
    }
    find_first_enabled(options) // Default to first enabled
  }

  let on_select = fn(idx : Int) { selected.set(options[idx].value) }
  // Build radios
  let radios : Array[@luna.Node[@js.Any, String]] = []
  i = 0
  while i < options.length() {
    let option = options[i]
    let index = i
    let radio_id = radio_ids[i]
    let keydown_handler = make_radio_keydown_handler(
      options, radio_ids, index, on_select,
    )
    let click_handler = @luna.handler(fn(_) {
      if !option.disabled {
        on_select(index)
      }
    })
    let attrs : Array[(String, @luna.Attr[@js.Any, String])] = [
      ("role", @luna.attr_static("radio")),
      ("id", @luna.attr_static(radio_id)),
      (
        "aria-checked",
        @luna.attr_dynamic(fn() {
          if selected.get() == option.value {
            "true"
          } else {
            "false"
          }
        }),
      ),
      (
        "tabindex",
        @luna.attr_dynamic(fn() {
          let sel_idx = find_selected_index()
          if index == sel_idx {
            "0"
          } else {
            "-1"
          }
        }),
      ),
      ("data-value", @luna.attr_static(option.value)),
      ("keydown", @luna.attr_handler(keydown_handler)),
      ("click", @luna.attr_handler(click_handler)),
    ]
    if option.disabled {
      attrs.push(("aria-disabled", @luna.attr_static("true")))
    }
    radios.push(@luna.h("div", attrs, option.label))
    i = i + 1
  }
  // Container
  let group_attrs : Array[(String, @luna.Attr[@js.Any, String])] = [
    ("role", @luna.attr_static("radiogroup")),
    ("id", @luna.attr_static(id)),
    ("data-name", @luna.attr_static(name)),
  ]
  if aria_label is Some(label) {
    group_attrs.push(("aria-label", @luna.attr_static(label)))
  }
  @luna.h("div", group_attrs, radios)
}