///|
fn newline_for(doc : Document) -> String {
  for line in scan_lines(doc.source) {
    if line.end > line.content_end {
      return text(doc.source, line.content_end, line.end)
    }
  }
  "\n"
}

///|
fn valid_names(section : String, key : String, dialect : Dialect) -> Bool {
  if key == "" ||
    key.contains("\r") ||
    key.contains("\n") ||
    section.contains("\r") ||
    section.contains("\n") {
    return false
  }
  let probe_source = if section == "" {
    key + "=x"
  } else {
    "[" + section + "]\n" + key + "=x"
  }
  let probe = parse(probe_source, dialect~)
  let expected = if section == "" { 1 } else { 2 }
  !probe.has_errors() &&
  probe.nodes.length() == expected &&
  probe.nodes[expected - 1].key == key &&
  probe.nodes[expected - 1].section == section &&
  probe.get(section, key) == Some("x")
}

///|
/// Append a new key to the last matching section block; create a section if absent.
/// Existing text remains an unchanged prefix/suffix; new lines use first observed EOL.
pub fn Document::insert(
  self : Document,
  section : String,
  key : String,
  value : String,
) -> Result[Document, Diagnostic] {
  if !editable(self) {
    return Err(edit_problem("EDIT001", "repair syntax errors before editing"))
  }
  if !valid_names(section, key, self.dialect) {
    return Err(
      edit_problem("EDIT005", "section or key cannot be represented safely"),
    )
  }
  if !self.entries(section, key).is_empty() {
    return Err(edit_problem("EDIT006", "key already exists; use set or set_at"))
  }
  let mut index = self.source.length()
  let mut found = section == ""
  let mut inside = section == ""
  for node in self.nodes {
    if node.kind == Section {
      if inside {
        index = node.span.start
        inside = false
      }
      if same_name(node.section, section, self.dialect) {
        found = true
        inside = true
        index = self.source.length()
      }
    }
  }
  let eol = newline_for(self)
  let mut added = ""
  if index > 0 &&
    self.source[index - 1] != '\n' &&
    self.source[index - 1] != '\r' &&
    !(index == 1 && self.source[0] == '\uFEFF') {
    added += eol
  }
  if !found {
    added += "[" + section + "]" + eol
  }
  added += key + "=" + encode_value(value, "", self.dialect) + eol
  let next = parse(
    text(self.source, 0, index) +
    added +
    text(self.source, index, self.source.length()),
    dialect=self.dialect,
    limits=self.limits,
  )
  if !editable(next) || next.get(section, key) != Some(value) {
    return Err(
      edit_problem("EDIT003", "insertion failed parse-back verification"),
    )
  }
  Ok(next)
}