/// Formats a `Duration` as a signed human-readable string (e.g. `"+1h30m"`, `"-5s"`).
///
/// Uses the largest possible units (weeks, days, hours, minutes, seconds, ms)
/// and omits zero-valued components. Zero duration is formatted as `"+0s"`.

///|
fn format_duration(d : Duration) -> String {
  let ms = d.0
  let sign = if ms < 0L { "-" } else { "+" }
  // Guard against Int64.min_value: -Int64.min_value overflows back to itself.
  // Handle by using Int64.max_value and carrying the extra 1ms into the final
  // milliseconds component. This works because max_value % 1000 == 807,
  // so adding 1 does not cause a carry into the seconds component.
  let extra_ms = if ms == @int64.MIN_VALUE { 1L } else { 0L }
  let mut remain = if ms == @int64.MIN_VALUE {
    @int64.MAX_VALUE
  } else if ms < 0L {
    -ms
  } else {
    ms
  }
  let buf = StringBuilder::new()
  buf.write_string(sign)
  if remain == 0L {
    buf.write_string("0s")
    return buf.to_string()
  }
  // weeks
  let weeks = remain / week.0
  remain = remain % week.0
  if weeks > 0L {
    buf.write_string(weeks.to_string())
    buf.write_char('w')
  }
  // days
  let days = remain / day.0
  remain = remain % day.0
  if days > 0L {
    buf.write_string(days.to_string())
    buf.write_char('d')
  }
  // hours
  let hours = remain / hour.0
  remain = remain % hour.0
  if hours > 0L {
    buf.write_string(hours.to_string())
    buf.write_char('h')
  }
  // minutes
  let minutes = remain / minute.0
  remain = remain % minute.0
  if minutes > 0L {
    buf.write_string(minutes.to_string())
    buf.write_char('m')
  }
  // seconds
  let seconds = remain / second.0
  remain = remain % second.0
  if seconds > 0L {
    buf.write_string(seconds.to_string())
    buf.write_char('s')
  }
  // milliseconds (add extra_ms for Int64.min_value correction)
  remain = remain + extra_ms
  if remain > 0L {
    buf.write_string(remain.to_string())
    buf.write_string("ms")
  }
  buf.to_string()
}

/// Re-serializes a `TimeSpec` into a CLI-friendly string.
///
/// - `Absolute` → ISO 8601 UTC datetime (e.g. `"2025-03-15T00:56:14Z"`).
/// - `Relative` → signed duration string (e.g. `"-8m"`, `"+1h30m"`).
///
/// Parameters:
///
/// * `self` : The `TimeSpec` to serialize.
/// * `epoch` : Epoch offset to add back to absolute values. Defaults to zero.
///
/// Returns a string suitable for passing to `--since` or `--until` CLI arguments.

///|
pub fn TimeSpec::to_cli_string(
  self : TimeSpec,
  epoch? : EpochTime = EpochTime(0L),
) -> String {
  match self {
    Absolute(epoch_time, _) => {
      let ms = epoch_time.0 + epoch.0
      epoch_to_iso8601(ms)
    }
    Relative(_, duration) => format_duration(duration)
  }
}