///|
fn sdk_string_arity(binding : String, context : String) -> Int32 raise {
  match binding {
    "replace"
    | "slice"
    | "pad"
    | "pad-left"
    | "pad-right"
    | "foldl"
    | "foldr" => 3
    "repeat"
    | "append"
    | "split"
    | "join"
    | "left"
    | "right"
    | "drop-left"
    | "drop-right"
    | "contains"
    | "starts-with"
    | "ends-with"
    | "indexes"
    | "indices"
    | "cons"
    | "map"
    | "filter"
    | "any"
    | "all" => 2
    "is-empty"
    | "length"
    | "reverse"
    | "concat"
    | "words"
    | "lines"
    | "to-int"
    | "from-int"
    | "to-float"
    | "from-float"
    | "from-char"
    | "uncons"
    | "to-list"
    | "from-list"
    | "to-upper"
    | "to-lower"
    | "trim"
    | "trim-left"
    | "trim-right" => 1
    _ =>
      raise Invalid(
        "moonbit.unsupported_sdk",
        context,
        "morphir/SDK:string#" + binding,
      )
  }
}

///|
/// Adapt the SDK's machine counts and uncurried folds to Morphir's concrete types.
fn sdk_string_call(
  ctx : Lowering,
  binding : String,
  inputs : Array[Lowered],
  result : Lowered,
  context : String,
) -> String raise {
  let canonical = "morphir/SDK:string#" + binding
  let text = Atom(Scalar::Text)
  let character = Atom(Scalar::Character)
  let int = Atom(Scalar::Integer)
  let float = Atom(Scalar::Float)
  let bool = Atom(Scalar::Boolean)
  let arguments = Array::makei(inputs.length(), i => "a" + i.to_string())
  fn require(signature : Array[Lowered], output : Lowered) -> Unit raise {
    if inputs != signature || result != output {
      raise Invalid("moonbit.sdk_signature", context, canonical)
    }
  }
  fn count(index : Int32) -> Unit {
    ctx.imports["integer"] = true
    arguments[index] = "(match @integer.to_int(a" +
      index.to_string() +
      ") { Some(n) => n; None => raise @sdk.OutOfRange(" +
      quote(canonical) +
      ") })"
  }
  match binding {
    "append" => require([text, text], text)
    "replace" => require([text, text, text], text)
    "is-empty" => require([text], bool)
    "length" => require([text], int)
    "reverse" | "to-upper" | "to-lower" | "trim" | "trim-left" | "trim-right" =>
      require([text], text)
    "concat" => require([Sequence(text)], text)
    "split" => require([text, text], Sequence(text))
    "join" => require([text, Sequence(text)], text)
    "words" | "lines" => require([text], Sequence(text))
    "repeat" | "left" | "right" | "drop-left" | "drop-right" => {
      require([int, text], text)
      count(0)
    }
    "slice" => {
      require([int, int, text], text)
      count(0)
      count(1)
    }
    "pad" | "pad-left" | "pad-right" => {
      require([int, character, text], text)
      count(0)
    }
    "contains" | "starts-with" | "ends-with" => require([text, text], bool)
    "indexes" | "indices" => require([text, text], Sequence(int))
    "to-int" => require([text], Optional(int))
    "from-int" => require([int], text)
    "to-float" => require([text], Optional(float))
    "from-float" => require([float], text)
    "from-char" => require([character], text)
    "cons" => require([character, text], text)
    "uncons" => require([text], Optional(Product([character, text])))
    "to-list" => require([text], Sequence(character))
    "from-list" => require([Sequence(character)], text)
    "map" => require([Arrow(character, character), text], text)
    "filter" => require([Arrow(character, bool), text], text)
    "any" | "all" => require([Arrow(character, bool), text], bool)
    "foldl" | "foldr" => {
      require([Arrow(character, Arrow(result, result)), result, text], result)
      arguments[0] = "fn(value, state) raise @sdk.SdkError { a0(value)(state) }"
    }
    _ => raise Invalid("moonbit.unsupported_sdk", context, canonical)
  }
  ctx.imports["string"] = true
  let function = binding
    .split("-")
    .map(StringView::to_owned)
    .collect()
    .join("_")
  let call = "@string." + function + "(" + arguments.join(", ") + ")"
  match binding {
    "length" => {
      ctx.imports["integer"] = true
      "@integer.from_int(" + call + ")"
    }
    "indexes" | "indices" => {
      ctx.imports["integer"] = true
      ctx.imports["list"] = true
      "@list.map(@integer.from_int, " + call + ")"
    }
    _ => call
  }
}