///|
/// ProgressBar - Progress indicator components

///|
/// Progress bar style
pub(all) enum ProgressStyle {
  Bar // [████████░░░░] 80%
  Dots // ●●●●●●●●○○○○
  Blocks // ▓▓▓▓▓▓▓▓░░░░
  Spinner // ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏
}

///|
/// Progress bar characters for different styles
fn get_progress_chars(style : ProgressStyle) -> (String, String) {
  match style {
    ProgressStyle::Bar => ("█", "░")
    ProgressStyle::Dots => ("●", "○")
    ProgressStyle::Blocks => ("▓", "░")
    ProgressStyle::Spinner => ("█", "░") // Spinner uses animation, not fill
  }
}

///|
/// Spinner frames for animation
let spinner_frames : Array[String] = [
  "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
]

///|
/// Get spinner frame for animation
pub fn get_spinner_frame(tick : Int) -> String {
  let idx = tick % spinner_frames.length()
  spinner_frames[idx]
}

///|
/// Build progress bar string
/// ratio: 0.0 to 1.0
/// width: total width in characters
pub fn build_progress_bar(
  ratio : Double,
  width : Int,
  style? : ProgressStyle = ProgressStyle::Bar,
  show_percent? : Bool = true,
) -> String {
  let clamped = if ratio < 0.0 {
    0.0
  } else if ratio > 1.0 {
    1.0
  } else {
    ratio
  }
  let percent_str = if show_percent {
    let pct = (clamped * 100.0).to_int()
    " " + pct.to_string() + "%"
  } else {
    ""
  }
  let bar_width = width - 2 - percent_str.length() // -2 for brackets
  if bar_width <= 0 {
    return percent_str.trim().to_string()
  }
  let filled = (bar_width.to_double() * clamped).to_int()
  let empty = bar_width - filled
  let (fill_char, empty_char) = get_progress_chars(style)
  "[" + fill_char.repeat(filled) + empty_char.repeat(empty) + "]" + percent_str
}

///|
/// Progress bar component with theme support
pub fn progress_bar(
  ratio : Double,
  width : Int,
  style? : ProgressStyle = ProgressStyle::Bar,
  show_percent? : Bool = true,
  theme? : Theme = Theme::default(),
) -> @core.Component {
  // Build the bar text
  let bar_text = build_progress_bar(ratio, width, style~, show_percent~)
  // Use primary color when filled, muted when empty
  text(bar_text, fg=if ratio > 0.0 { theme.primary } else { theme.muted })
}

///|
/// Spinner component with theme support (use with ticker for animation)
pub fn spinner(
  tick : Int,
  label? : String = "",
  theme? : Theme = Theme::default(),
) -> @core.Component {
  let frame = get_spinner_frame(tick)
  if label.length() > 0 {
    row([text(frame + " ", fg=theme.primary), text(label, fg=theme.fg)])
  } else {
    text(frame, fg=theme.primary)
  }
}

///|
/// Loading indicator with dots animation and theme support
pub fn loading_dots(
  tick : Int,
  label? : String = "Loading",
  theme? : Theme = Theme::default(),
) -> @core.Component {
  let dots = match tick % 4 {
    0 => ""
    1 => "."
    2 => ".."
    _ => "..."
  }
  text(label + dots, fg=theme.fg)
}

///|
/// Simple percentage text with theme support
pub fn percent_text(
  ratio : Double,
  theme? : Theme = Theme::default(),
) -> @core.Component {
  let clamped = if ratio < 0.0 {
    0.0
  } else if ratio > 1.0 {
    1.0
  } else {
    ratio
  }
  let pct = (clamped * 100.0).to_int()
  text(pct.to_string() + "%", fg=theme.fg)
}

///|
/// Fraction text with theme support (e.g., "5/10")
pub fn fraction_text(
  current : Int,
  total : Int,
  theme? : Theme = Theme::default(),
) -> @core.Component {
  text(current.to_string() + "/" + total.to_string(), fg=theme.fg)
}