///|
/// 判断字符串是否只包含十进制数字以及常见分隔符(空格、连字符、制表符)。
fn only_digits_and_separators(s : String) -> Bool {
  let b = @utf8.encode(s)
  for i = 0; i < b.length(); i = i + 1 {
    let c = b[i]
    if !((c >= b'0' && c <= b'9') || c == b' ' || c == b'-' || c == b'\t') {
      return false
    }
  }
  true
}

///|
/// 去掉输入中的空白与常见分隔符(`-`、空格等),只保留数字和字母 X。
pub fn strip_separators(s : String) -> String {
  let b = @utf8.encode(s)
  let out = Buffer()
  for i = 0; i < b.length(); i = i + 1 {
    let c = b[i]
    if (c >= b'0' && c <= b'9') || c == b'X' || c == b'x' {
      out.write_byte(c)
    }
  }
  @utf8.decode_lossy(out.to_bytes())
}

///|
/// 判断字符串是否只包含十进制数字。
pub fn is_all_digits(s : String) -> Bool {
  let b = @utf8.encode(s)
  if b.length() == 0 {
    return false
  }
  for i = 0; i < b.length(); i = i + 1 {
    let c = b[i]
    if c < b'0' || c > b'9' {
      return false
    }
  }
  true
}

///|
/// 计算 mod-10 校验位。
///
/// 对从左到右的第奇数位乘以 `odd_weight`、偶数位乘以 `even_weight` 求和,
/// 校验位 = `(10 - sum % 10) % 10`。适用于 EAN-13 / EAN-8 / UPC-A / GTIN-14
/// 等以 10 为模的校验族(权重按各符号体系的规范传入)。
pub fn mod10_check(
  digits : String,
  odd_weight : Int,
  even_weight : Int,
) -> Result[Int, String] {
  if !only_digits_and_separators(digits) {
    return Err("payload must contain only decimal digits and separators")
  }
  let d = strip_separators(digits)
  if d.length() == 0 {
    return Err("payload must not be empty")
  }
  if !is_all_digits(d) {
    return Err("payload must contain only decimal digits")
  }
  let b = @utf8.encode(d)
  let mut sum = 0
  for i = 0; i < b.length(); i = i + 1 {
    let v = (b[i] - b'0').to_int()
    let weight = if (i + 1) % 2 == 1 { odd_weight } else { even_weight }
    sum = sum + v * weight
  }
  Ok((10 - sum % 10) % 10)
}

///|
/// 验证包含校验位的完整数字串(mod-10 校验族)。
pub fn mod10_verify(
  digits : String,
  odd_weight : Int,
  even_weight : Int,
) -> Bool {
  let d = strip_separators(digits)
  if d.length() < 2 || !is_all_digits(d) {
    return false
  }
  let b = @utf8.encode(d)
  let mut sum = 0
  for i = 0; i < b.length(); i = i + 1 {
    let v = (b[i] - b'0').to_int()
    let weight = if (i + 1) % 2 == 1 { odd_weight } else { even_weight }
    sum = sum + v * weight
  }
  sum % 10 == 0
}