///|
/// Msg-generic Markdown outline inspector, extracted from MoMark's chrome.
/// The caller owns folding state and receives jump/fold intents; the panel
/// renders the same 240pt inspector with heading rows indented by level.

///|
/// Outline inspector width (MoMark parity).
const MARKDOWN_OUTLINE_PANEL_WIDTH : Double = 240.0

///|
const MARKDOWN_OUTLINE_CONTENT_WIDTH : Double = 212.0

///|
/// One outline row: indented label button, Tonal when it is the active
/// heading, with a long-press affordance for folding.
fn[Msg] markdown_outline_row(
  item : @richtext.MarkdownEditorOutlineItem,
  folded : Bool,
  active : Bool,
  on_jump~ : (Int) -> Msg,
  on_toggle_fold~ : (Int) -> Msg,
  theme~ : @moui.Theme,
) -> @moui.View[Msg] {
  let indent = markdown_outline_indent_width(item.level)
  let label = if folded {
    "▸ " + markdown_outline_label(item)
  } else {
    markdown_outline_label(item)
  }
  let variant = if active {
    @views.ButtonVariant::Tonal
  } else {
    @views.ButtonVariant::Ghost
  }
  @views.padding_edges(
    @views.button(
      label,
      on_click=on_jump(item.source_start),
      variant~,
      theme~,
      width=(MARKDOWN_OUTLINE_CONTENT_WIDTH - indent).max(96.0),
      height=30.0,
    ).on_long_press(on_toggle_fold(item.source_start)),
    left=indent,
  )
}

///|
/// The outline inspector for a live session. `folded_starts` lists heading
/// `source_start`s whose children are hidden; rows jump via `on_jump` and
/// toggle folding via `on_toggle_fold`.
pub fn[Msg] markdown_outline_panel(
  theme~ : @moui.Theme,
  outline~ : Array[@richtext.MarkdownEditorOutlineItem],
  folded_starts~ : Array[Int],
  caret~ : Int,
  on_jump~ : (Int) -> Msg,
  on_toggle_fold~ : (Int) -> Msg,
  height~ : Double,
  width? : Double = MARKDOWN_OUTLINE_PANEL_WIDTH,
  title? : String = "Outline",
  empty_label? : String = "No headings",
) -> @moui.View[Msg] {
  let content_width = (width - 28.0).max(96.0)
  let visible_outline = markdown_folded_outline(outline, folded_starts)
  let active_index = markdown_active_outline_index(outline, caret)
  let content = if visible_outline.length() == 0 {
    @views.text(empty_label, width=content_width, height=28.0, theme~)
    .font(theme.typography.caption)
    .foreground(theme.palette.muted)
  } else {
    let rows : Array[@moui.View[Msg]] = []
    for item in visible_outline {
      let active = markdown_outline_item_is_active(outline, item, active_index)
      rows.push(
        markdown_outline_row(
          item,
          folded_starts.contains(item.source_start),
          active,
          on_jump~,
          on_toggle_fold~,
          theme~,
        ),
      )
    }
    @views.scroll_view(
      @views.column(
        rows,
        spacing=theme.spacing_scale.xs,
        align=@views.CrossAlign::Start,
      ),
      width=content_width,
      height=(height - 52.0).max(0.0),
    )
  }
  @views.container(
    @views.padding_edges(
      @views.column(
        [
          @views.text(title, width=content_width, height=24.0, theme~)
          .font(theme.typography.caption)
          .foreground(theme.palette.muted),
          content,
        ],
        spacing=theme.spacing_scale.sm,
        align=@views.CrossAlign::Start,
      ),
      top=theme.spacing_scale.md,
      right=theme.spacing_scale.sm,
      bottom=theme.spacing_scale.md,
      left=theme.spacing_scale.sm,
    ),
    width~,
    height~,
    variant=@views.ContainerVariant::Base,
    theme~,
    background=theme.palette.surface,
    corner_radius=0.0,
    padding=0.0,
  )
}