///|
/// Exact IPv6 VRP records added and removed between two snapshots.
pub(all) struct Ipv6SnapshotDiff {
  added : Array[Ipv6VrpRecord]
  removed : Array[Ipv6VrpRecord]
  unchanged : Int
}

///|
pub fn Ipv6SnapshotDiff::is_empty(self : Ipv6SnapshotDiff) -> Bool {
  self.added.is_empty() && self.removed.is_empty()
}

///|
pub fn Ipv6SnapshotDiff::summary(self : Ipv6SnapshotDiff) -> String {
  "+\{self.added.length()} -\{self.removed.length()} (\{self.unchanged} unchanged)"
}

///|
struct Ipv6VrpRecordKey {
  prefix : Ipv6Prefix
  max_length : Int
  asn : UInt
  trust_anchor : String
} derive(Eq, Hash)

///|
pub extend Ipv6VrpRecordKey with Eq::{not_equal, equal}

///|
pub extend Ipv6VrpRecordKey with Hash::{hash, hash_combine}

///|
fn Ipv6VrpRecord::ipv6_key(self : Ipv6VrpRecord) -> Ipv6VrpRecordKey {
  {
    prefix: self.vrp.prefix(),
    max_length: self.vrp.max_length(),
    asn: self.vrp.asn(),
    trust_anchor: self.trust_anchor,
  }
}

///|
fn unique_ipv6_records(records : Array[Ipv6VrpRecord]) -> Array[Ipv6VrpRecord] {
  let unique : Array[Ipv6VrpRecord] = []
  let seen : Map[Ipv6VrpRecordKey, Unit] = Map([], capacity=records.length())
  for record in records {
    let key = record.ipv6_key()
    if !seen.contains(key) {
      seen.set(key, ())
      unique.push(record)
    }
  }
  unique
}

///|
fn ipv6_record_keys(
  records : Array[Ipv6VrpRecord],
) -> Map[Ipv6VrpRecordKey, Unit] {
  let keys : Map[Ipv6VrpRecordKey, Unit] = Map([], capacity=records.length())
  for record in records {
    keys.set(record.ipv6_key(), ())
  }
  keys
}

///|
/// Compare IPv6 VRP records with set semantics and trust-anchor identity.
///
/// Added records retain their order in current; removed records retain their
/// order in previous. Duplicate exact records count only once.
pub fn compare_ipv6_snapshots(
  previous : Array[Ipv6VrpRecord],
  current : Array[Ipv6VrpRecord],
) -> Ipv6SnapshotDiff {
  let before = unique_ipv6_records(previous)
  let after = unique_ipv6_records(current)
  let before_keys = ipv6_record_keys(before)
  let after_keys = ipv6_record_keys(after)
  let added : Array[Ipv6VrpRecord] = []
  let removed : Array[Ipv6VrpRecord] = []
  let mut unchanged = 0
  for record in after {
    if before_keys.contains(record.ipv6_key()) {
      unchanged = unchanged + 1
    } else {
      added.push(record)
    }
  }
  for record in before {
    if !after_keys.contains(record.ipv6_key()) {
      removed.push(record)
    }
  }
  { added, removed, unchanged, }
}