///|
pub(all) struct BundleAnalytics {
  total_entries : Int
  resource_count : Int
  unique_ids : Int
  duplicate_ids : Array[String]
  type_counts : Map[String, Int]
  references : Array[String]
  broken_references : Array[String]
} derive(Debug, Eq, ToJson)

///|
fn bundle_increment(map : Map[String, Int], key : String) -> Unit {
  map[key] = map.get(key).unwrap_or(0) + 1
}

///|
fn bundle_reference_key(value : Json) -> String? {
  match resource_type(value) {
    None => None
    Some(kind) =>
      match field_string(value, "id") {
        Some(id) => Some("\{kind}/\{id}")
        None => None
      }
  }
}

///|
pub fn analyze_bundle(bundle : Json) -> BundleAnalytics {
  let entries = field_array(bundle, "entry").unwrap_or([])
  let type_counts : Map[String, Int] = Map([])
  let ids : Map[String, Int] = Map([])
  let duplicate_ids : Array[String] = []
  let references : Array[String] = []
  let resources : Array[Json] = []
  for entry in entries {
    match object_get(entry, "resource") {
      None => ()
      Some(resource) => {
        resources.push(resource)
        bundle_increment(type_counts, resource_type(resource).unwrap_or(""))
        match bundle_reference_key(resource) {
          Some(key) => {
            ids[key] = ids.get(key).unwrap_or(0) + 1
            if ids[key] == 2 {
              duplicate_ids.push(key)
            }
          }
          None => ()
        }
        references.append(query_resource_references(resource)[:])
      }
    }
  }
  let broken_references : Array[String] = []
  for reference in references {
    if !ids.contains(reference) && !broken_references.contains(reference) {
      broken_references.push(reference)
    }
  }
  {
    total_entries: entries.length(),
    resource_count: resources.length(),
    unique_ids: ids.length(),
    duplicate_ids,
    type_counts,
    references,
    broken_references,
  }
}

///|
pub fn bundle_type_count(bundle : Json, kind : String) -> Int {
  analyze_bundle(bundle).type_counts.get(kind).unwrap_or(0)
}

///|
pub fn bundle_duplicate_count(bundle : Json) -> Int {
  analyze_bundle(bundle).duplicate_ids.length()
}

///|
pub fn bundle_reference_count(bundle : Json) -> Int {
  analyze_bundle(bundle).references.length()
}

///|
pub fn bundle_is_consistent(bundle : Json) -> Bool {
  analyze_bundle(bundle).broken_references.length() == 0 &&
  bundle_duplicate_count(bundle) == 0
}

///|
pub fn bundle_analytics_summary(bundle : Json) -> String {
  let stats = analyze_bundle(bundle)
  "entries=\{stats.total_entries} resources=\{stats.resource_count} unique_ids=\{stats.unique_ids} duplicates=\{stats.duplicate_ids.length()} references=\{stats.references.length()} broken=\{stats.broken_references.length()}"
}