///|
/// A named alternative history forked from a journal boundary.
pub struct ReplayBranch[P] {
  name : String
  base_sequence : Int
  journal : Journal[P]
}

///|
/// Forks a branch from the first `sequence` events of a source journal.
///
/// Returns `None` when the source is invalid or the boundary is out of range.
pub fn[P] ReplayBranch::fork(
  name : String,
  source : Journal[P],
  sequence : Int,
  payload_fingerprint : (P) -> String,
) -> ReplayBranch[P]? {
  if sequence < 0 || sequence > source.length() {
    return None
  }
  match source.validate(payload_fingerprint) {
    Invalid(_) => None
    Valid(_, _) =>
      Some({ name, base_sequence: sequence, journal: source.prefix(sequence) })
  }
}

///|
/// Returns the branch name.
pub fn[P] ReplayBranch::name(self : ReplayBranch[P]) -> String {
  self.name
}

///|
/// Returns the source sequence where the branch was created.
pub fn[P] ReplayBranch::base_sequence(self : ReplayBranch[P]) -> Int {
  self.base_sequence
}

///|
/// Returns a copy of the branch journal.
pub fn[P] ReplayBranch::journal(self : ReplayBranch[P]) -> Journal[P] {
  Journal::from_events(self.journal.events())
}

///|
/// Appends one event to the alternative history.
pub fn[P] ReplayBranch::append(
  self : ReplayBranch[P],
  kind : String,
  payload : P,
  correlation_id : String,
  payload_fingerprint : (P) -> String,
) -> JournalEvent[P] {
  self.journal.append(kind, payload, correlation_id, payload_fingerprint)
}

///|
/// Locates the first structural difference between two event histories.
pub fn[P] compare_histories(
  left : Journal[P],
  right : Journal[P],
  payload_fingerprint : (P) -> String,
) -> Divergence? {
  match left.validate(payload_fingerprint) {
    Invalid(_) =>
      return Some({
        sequence: 0,
        kind: EventMismatch,
        left_hash: left.tail_hash(),
        right_hash: right.tail_hash(),
        message: "left journal is invalid",
      })
    Valid(_, _) => ()
  }
  match right.validate(payload_fingerprint) {
    Invalid(_) =>
      return Some({
        sequence: 0,
        kind: EventMismatch,
        left_hash: left.tail_hash(),
        right_hash: right.tail_hash(),
        message: "right journal is invalid",
      })
    Valid(_, _) => ()
  }
  let common = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  for index = 0; index < common; index = index + 1 {
    let left_event = match left.get(index) {
      Some(event) => event
      None => abort("validated left event disappeared")
    }
    let right_event = match right.get(index) {
      Some(event) => event
      None => abort("validated right event disappeared")
    }
    if left_event.hash != right_event.hash {
      return Some({
        sequence: index + 1,
        kind: EventMismatch,
        left_hash: left_event.hash,
        right_hash: right_event.hash,
        message: "event histories diverge",
      })
    }
  }
  if left.length() != right.length() {
    return Some({
      sequence: common + 1,
      kind: LengthMismatch,
      left_hash: left.tail_hash(),
      right_hash: right.tail_hash(),
      message: "event histories have different lengths",
    })
  }
  None
}