///|
/// ScrollableView - renders content with vertical scrolling
/// Instead of using crater layout, renders content as lines and shows
/// only the visible portion based on scroll offset

///|
/// A line with color styling
pub(all) struct StyledLine {
  content : String
  fg : @core.Color
  bold : Bool
}

///|
pub fn StyledLine::plain(content : String) -> StyledLine {
  { content, fg: @core.Color::white(), bold: false }
}

///|
pub fn StyledLine::new(
  content : String,
  fg? : @core.Color = @core.Color::white(),
  bold? : Bool = false,
) -> StyledLine {
  { content, fg, bold }
}

///|
/// ScrollableView state
pub(all) struct ScrollableView {
  lines : Ref[Array[StyledLine]]
  offset : @signals.Signal[Int]
  auto_scroll : @signals.Signal[Bool]
  width : Ref[Int]
  visible_height : Ref[Int]
}

///|
pub fn ScrollableView::new(width : Int, visible_height : Int) -> ScrollableView {
  {
    lines: { val: [] },
    offset: @signals.signal(0),
    auto_scroll: @signals.signal(true),
    width: { val: width },
    visible_height: { val: visible_height },
  }
}

///|
pub fn ScrollableView::resize(
  self : ScrollableView,
  width : Int,
  visible_height : Int,
) -> Unit {
  self.width.val = width
  self.visible_height.val = visible_height
  // Adjust offset if needed
  let max = self.max_offset()
  if self.offset.get() > max {
    self.offset.set(max)
  }
}

///|
/// Set content lines
pub fn ScrollableView::set_lines(
  self : ScrollableView,
  lines : Array[StyledLine],
) -> Unit {
  self.lines.val = lines
  if self.auto_scroll.get() {
    self.to_bottom()
  }
}

///|
/// Get total line count
pub fn ScrollableView::total_lines(self : ScrollableView) -> Int {
  self.lines.val.length()
}

///|
/// Get max scroll offset
pub fn ScrollableView::max_offset(self : ScrollableView) -> Int {
  let total = self.total_lines()
  let visible = self.visible_height.val
  if total > visible {
    total - visible
  } else {
    0
  }
}

///|
/// Scroll up by n lines
pub fn ScrollableView::up(self : ScrollableView, n : Int) -> Bool {
  let current = self.offset.get()
  if current > 0 {
    let new_offset = if current >= n { current - n } else { 0 }
    self.offset.set(new_offset)
    self.auto_scroll.set(false)
    true
  } else {
    false
  }
}

///|
/// Scroll down by n lines
pub fn ScrollableView::down(self : ScrollableView, n : Int) -> Bool {
  let max = self.max_offset()
  let current = self.offset.get()
  if current < max {
    let new_offset = if current + n > max { max } else { current + n }
    self.offset.set(new_offset)
    if new_offset >= max {
      self.auto_scroll.set(true)
    }
    true
  } else {
    false
  }
}

///|
/// Jump to bottom
pub fn ScrollableView::to_bottom(self : ScrollableView) -> Unit {
  self.offset.set(self.max_offset())
  self.auto_scroll.set(true)
}

///|
/// Follow bottom (auto-scroll when enabled)
pub fn ScrollableView::follow_bottom(self : ScrollableView) -> Unit {
  if self.auto_scroll.get() {
    self.to_bottom()
  }
}

///|
/// Check if can scroll up
pub fn ScrollableView::can_scroll_up(self : ScrollableView) -> Bool {
  self.offset.get() > 0
}

///|
/// Check if can scroll down
pub fn ScrollableView::can_scroll_down(self : ScrollableView) -> Bool {
  self.offset.get() < self.max_offset()
}

///|
/// Get visible height
pub fn ScrollableView::get_visible_height(self : ScrollableView) -> Int {
  self.visible_height.val
}

///|
/// Get width
pub fn ScrollableView::get_width(self : ScrollableView) -> Int {
  self.width.val
}

///|
/// Wrap text to fit within width, handling newlines
pub fn wrap_text(text : String, width : Int) -> Array[String] {
  let result : Array[String] = []
  if width <= 0 {
    result.push(text)
    return result
  }

  // Split by newlines first
  let paragraphs = text.split("\n").collect()
  for para in paragraphs {
    if para.length() == 0 {
      result.push("")
    } else {
      // Wrap this paragraph
      let line = StringBuilder::new()
      let mut col = 0
      for c in para {
        let cw = @core.char_display_width(c)
        if col + cw > width && col > 0 {
          result.push(line.to_string())
          line.reset()
          col = 0
        }
        line.write_char(c)
        col = col + cw
      }
      if col > 0 {
        result.push(line.to_string())
      }
    }
  }
  if result.is_empty() {
    result.push("")
  }
  result
}