///|
/// 保存一次已经在 MoonBit 栈上完成规范化的同步补全结果。
priv struct SessionCompletionResponse {
  candidates : FixedArray[Bytes]
  replacement : Bytes?
}

///|
fn normalize_session_candidates(
  candidates : Array[String],
) -> Array[String] raise ReadlineError {
  let normalized : Array[String] = []
  for candidate in candidates {
    guard !candidate.contains("\u{0}") else { raise EmbeddedNul }
    if candidate != "" && !normalized.contains(candidate) {
      normalized.push(candidate)
    }
  }
  normalized
}

///|
fn session_common_prefix(left : String, right : String) -> String {
  let left = left.iter().collect()
  let right = right.iter().collect()
  let length = left.length().min(right.length())
  let common_length = for index = 0; index < length; index = index + 1 {
    if left[index] != right[index] {
      break index
    }
  } nobreak {
    length
  }
  let result = StringBuilder()
  for index in 0.. String? {
  match candidates {
    [] => None
    [candidate] => Some(candidate)
    [first, .. rest] => {
      let prefix = rest.fold(init=first, session_common_prefix)
      if prefix.has_prefix(current_word) &&
        prefix.char_length() > current_word.char_length() {
        Some(prefix)
      } else {
        None
      }
    }
  }
}

///|
#warnings("-unused_value")
fn make_session_completion_response(
  completer : (CompletionContext) -> Array[String],
  context : CompletionContext,
) -> SessionCompletionResponse raise ReadlineError {
  let candidates = normalize_session_candidates(completer(context))
  let replacement = session_completion_replacement(
    candidates,
    context.current_word,
  ).map(replacement => @utf8.encode(replacement))
  let encoded = candidates.map(candidate => @utf8.encode(candidate))
  { candidates: FixedArray::from_array(encoded), replacement }
}

///|
#warnings("-unused_value")
fn EditorOwner::session_completion_context(
  self : EditorOwner,
  generation : Int64,
) -> CompletionContext raise ReadlineError {
  let before = self.session_completion_before(generation)
  let after = self.session_completion_after(generation)
  let word = self.session_completion_word(generation)
  guard self.session_operation_status() == 0 else {
    raise ReadFailed(self.session_errno())
  }
  let before_cursor = @utf8.decode(before) catch { _ => raise InvalidUtf8 }
  let after_cursor = @utf8.decode(after) catch { _ => raise InvalidUtf8 }
  let current_word = @utf8.decode(word) catch { _ => raise InvalidUtf8 }
  { before_cursor, after_cursor, current_word }
}

///|
#warnings("-unused_value")
fn EditorOwner::session_apply_completion(
  self : EditorOwner,
  generation : Int64,
  response : SessionCompletionResponse,
) -> Unit raise ReadlineError {
  let (replacement, has_replacement) = match response.replacement {
    Some(replacement) => (replacement, true)
    None => (Bytes::new(0), false)
  }
  match
    self.session_resume_completion(
      generation,
      response.candidates,
      replacement,
      has_replacement,
    ) {
    0 => ()
    1 => raise Busy
    2 => raise EmbeddedNul
    5 => raise CleanupFailed(self.session_errno())
    _ => raise ReadFailed(self.session_errno())
  }
}