///|
priv struct CaptureWindowV5 {
  unlocated : Bool
  closed : Map[String, Bool]
  uncertain_after : Map[String, Int64]
  complete : Bool
}

///|
/// Sequence holes may contain operations on every transaction then active.
/// Producer-finality and known local prefixes are separate source properties.
fn capture_window_v5(
  history : HistoryV4,
  normalized : Json,
  charge : () -> Bool,
) -> CaptureWindowV5 {
  let n = object_fields_unchecked_v3(normalized)
  let root = object_fields_unchecked_v3(history.source_document)
  let unlocated = !capture_source_array_v4(
    root.get_or_default("gaps", Json::null()),
  ).is_empty()
  let missing : Array[Int64] = []
  let unknown : Map[String, Int64] = Map([])
  let closed : Map[String, Bool] = Map([])
  let cutoff : Map[String, Int64] = Map([])
  let verified_statements : Map[Int64, Bool] = Map([])
  let verified_facts : Map[Int64, Bool] = Map([])
  let proof = object_fields_unchecked_v3(
    n.get_or_default("sqlProof", Json::null()),
  )
  for
    seq in capture_source_array_v4(
      proof.get_or_default("verifiedStatements", Json::null()),
    ) {
    if !charge() {
      return { unlocated, closed, uncertain_after: cutoff, complete: false, }
    }
    verified_statements[capture_source_seq_v4(seq)] = true
  }
  for
    seq in capture_source_array_v4(
      proof.get_or_default("verifiedFacts", Json::null()),
    ) {
    if !charge() {
      return { unlocated, closed, uncertain_after: cutoff, complete: false, }
    }
    verified_facts[capture_source_seq_v4(seq)] = true
  }
  let mut expected = 1L
  for event in capture_source_array_v4(n.at("observations")) {
    if !charge() {
      return { unlocated, closed, uncertain_after: cutoff, complete: false, }
    }
    let e = object_fields_unchecked_v3(event)
    let seq = capture_source_seq_v4(e.at("seq"))
    if seq != expected {
      missing.push(expected)
    }
    expected = seq + 1L
    let kind = e.at("kind")
    let unproved = (
        kind == Json("statement") && !verified_statements.contains(seq)
      ) ||
      (kind == Json("condition") && !verified_facts.contains(seq)) ||
      (kind == Json("rows") && e.get("complete") == Some(Json(false)))
    if unproved {
      match e.get("tx") {
        Some(String(tx)) =>
          match unknown.get(tx) {
            Some(first) if first <= seq => ()
            _ => unknown[tx] = seq
          }
        _ => ()
      }
    }
  }
  for raw in capture_source_array_v4(n.at("transactions")) {
    if !charge() {
      return { unlocated, closed, uncertain_after: cutoff, complete: false, }
    }
    let tx = object_fields_unchecked_v3(raw)
    let id = capture_source_string_v4(tx.at("id"))
    let start = capture_source_seq_v4(tx.at("beginSeq"))
    let end = match tx.at("endSeq") {
      Number(n, ..) => Some(n.to_int64())
      _ => None
    }
    let mut first = unknown.get(id)
    for hole in missing {
      if !charge() {
        return { unlocated, closed, uncertain_after: cutoff, complete: false, }
      }
      if hole > start &&
        (end is None || hole <= end.unwrap()) &&
        (first is None || hole < first.unwrap()) {
        first = Some(hole)
      }
    }
    if unlocated {
      first = Some(start)
    }
    match first {
      Some(at) => cutoff[id] = at
      _ => ()
    }
    let confirmed = tx.at("status") == Json("committed") ||
      tx.at("status") == Json("aborted")
    closed[id] = confirmed &&
      !unlocated &&
      end is Some(last) &&
      (first is None || first.unwrap() > last)
  }
  { unlocated, closed, uncertain_after: cutoff, complete: true, }
}

///|
fn capture_prefix_known_v5(
  window : CaptureWindowV5?,
  tx : String,
  at : Int64,
) -> Bool {
  match window {
    Some(window) if window.complete && !window.unlocated =>
      match window.uncertain_after.get(tx) {
        Some(first) => at < first
        None => true
      }
    _ => false
  }
}