///|
fn spaces(count : Int) -> String {
  if count <= 0 {
    ""
  } else {
    String::from_array(Array::make(count, ' '))
  }
}

///|
/// Pad a string on the right to a minimum width.
pub fn pad_right(value : String, width : Int) -> String {
  if value.length() >= width {
    value
  } else {
    value + spaces(width - value.length())
  }
}

///|
fn row_width(row : Array[String], index : Int) -> Int {
  if index < row.length() {
    row[index].length()
  } else {
    0
  }
}

///|
fn table_widths(rows : Array[Array[String]]) -> Array[Int] {
  let widths = []
  for row in rows {
    for i in 0.. widths[i] {
        widths[i] = row_width(row, i)
      }
    }
  }
  widths
}

///|
fn render_row(row : Array[String], widths : Array[Int]) -> String {
  let mut output = ""
  for i in 0.. 0 {
      output = output + " | "
    }
    let is_last = i == row.length() - 1
    let cell = if is_last { row[i] } else { pad_right(row[i], widths[i]) }
    output = output + cell
  }
  output
}

///|
/// Render a small aligned text table with no hidden sorting.
pub fn render_text_table(rows : Array[Array[String]]) -> String {
  if rows.length() == 0 {
    ""
  } else {
    let widths = table_widths(rows)
    let mut output = ""
    for row in rows {
      output = output + render_row(row, widths) + "\n"
    }
    output
  }
}

///|
/// Return the maximum number of columns in a table.
pub fn table_column_count(rows : Array[Array[String]]) -> Int {
  let mut result = 0
  for row in rows {
    if row.length() > result {
      result = row.length()
    }
  }
  result
}

///|
/// Return whether all rows have the same number of columns.
pub fn table_is_rectangular(rows : Array[Array[String]]) -> Bool {
  if rows.length() == 0 {
    true
  } else {
    let expected = rows[0].length()
    let mut result = true
    for row in rows {
      if row.length() != expected {
        result = false
      }
    }
    result
  }
}

///|
/// Render plan steps as an aligned table.
pub fn render_plan_table(plan : MeasurementPlan) -> String {
  let rows = [["feature", "device", "strategy", "direction", "uncertainty"]]
  for step in plan.steps {
    rows.push([
      step.feature_id,
      step.device_id,
      strategy_text(step.strategy),
      step.direction,
      step.uncertainty.to_string(),
    ])
  }
  render_text_table(rows)
}

///|
/// Render dashboard cards as an aligned table.
pub fn render_dashboard_table(dashboard : QualityDashboard) -> String {
  let rows = [["key", "label", "value", "status"]]
  for card in dashboard.cards {
    rows.push([card.key, card.label, card.value, card.status])
  }
  render_text_table(rows)
}

///|
/// Render diagnostic records as an aligned table.
pub fn render_diagnostics_table(diagnostics : Array[Diagnostic]) -> String {
  let rows = [["severity", "code", "index", "message"]]
  for diagnostic in diagnostics {
    rows.push([
      severity_text(diagnostic.severity),
      diagnostic.code,
      diagnostic.index.to_string(),
      diagnostic.message,
    ])
  }
  render_text_table(rows)
}

///|
/// Render key-value rows while preserving input order.
pub fn render_key_values(values : Array[(String, String)]) -> String {
  let rows = []
  for pair in values {
    rows.push([pair.0, pair.1])
  }
  render_text_table(rows)
}

///|
/// Truncate text with an ellipsis when it exceeds a limit.
pub fn truncate_text(value : String, limit : Int) -> String {
  if limit <= 0 {
    ""
  } else if value.length() <= limit {
    value
  } else if limit == 1 {
    "…"
  } else {
    let chars = value.to_array()
    let prefix = []
    for i in 0..<(limit - 1) {
      prefix.push(chars[i])
    }
    String::from_array(prefix) + "…"
  }
}

///|
/// Render a one-line metric with a stable key.
pub fn render_metric(key : String, value : Double) -> String {
  key + "=" + value.to_string()
}