///|
/// Aggregate operational metadata. Values and keys are not included except
/// for the most revised key, so callers can log counts separately from data.
pub struct LedgerStats {
  revisions : Int32
  keys : Int32
  batches : Int32
  tombstones : Int32
  open_valid_spans : Int32
  most_revised_key : String?
  most_revisions_for_key : Int32
  first_knowledge_time : Int64?
  latest_knowledge_time : Int64?
}

///|
pub fn LedgerStats::revisions(self : LedgerStats) -> Int32 {
  self.revisions
}

///|
pub fn LedgerStats::keys(self : LedgerStats) -> Int32 {
  self.keys
}

///|
pub fn LedgerStats::batches(self : LedgerStats) -> Int32 {
  self.batches
}

///|
pub fn LedgerStats::tombstones(self : LedgerStats) -> Int32 {
  self.tombstones
}

///|
pub fn LedgerStats::open_valid_spans(self : LedgerStats) -> Int32 {
  self.open_valid_spans
}

///|
pub fn LedgerStats::most_revised_key(self : LedgerStats) -> String? {
  self.most_revised_key
}

///|
pub fn LedgerStats::most_revisions_for_key(self : LedgerStats) -> Int32 {
  self.most_revisions_for_key
}

///|
pub fn LedgerStats::first_knowledge_time(self : LedgerStats) -> Int64? {
  self.first_knowledge_time
}

///|
pub fn LedgerStats::latest_knowledge_time(self : LedgerStats) -> Int64? {
  self.latest_knowledge_time
}

///|
/// Scan the append log once for statistics useful to storage adapters.
pub fn Ledger::stats(self : Ledger) -> LedgerStats {
  let counts : Map[String, Int32] = Map([])
  let mut batches = 0
  let mut tombstones = 0
  let mut open_valid_spans = 0
  let mut most_revised_key : String? = None
  let mut most_revisions_for_key = 0
  let mut previous_time : Int64? = None
  for revision in self.revisions {
    if previous_time != Some(revision.known_at) {
      batches = batches + 1
      previous_time = Some(revision.known_at)
    }
    if revision.value is None {
      tombstones = tombstones + 1
    }
    if revision.valid.end is None {
      open_valid_spans = open_valid_spans + 1
    }
    let count = match counts.get(revision.key) {
      Some(previous) => previous + 1
      None => 1
    }
    counts[revision.key] = count
    if count > most_revisions_for_key ||
      (
        count == most_revisions_for_key &&
        (match most_revised_key {
          Some(previous) => revision.key.lexical_compare(previous) < 0
          None => true
        })
      ) {
      most_revisions_for_key = count
      most_revised_key = Some(revision.key)
    }
  }
  let first_knowledge_time = if self.revisions.is_empty() {
    None
  } else {
    Some(self.revisions[0].known_at)
  }
  {
    revisions: self.revisions.length(),
    keys: counts.length(),
    batches,
    tombstones,
    open_valid_spans,
    most_revised_key,
    most_revisions_for_key,
    first_knowledge_time,
    latest_knowledge_time: self.latest_time,
  }
}