// Copyright 2026 Leo Cheng
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Log compaction (ยง7). The leader folds the prefix of its log into a snapshot
/// so the live log stops growing without bound. Here the leader compacts, then a
/// follower that was down while the leader kept appending restarts and catches
/// up from the compacted log - the snapshot is exactly the point a lagging node
/// is served from by the leader's automatic InstallSnapshot fallback.
///
///     moon run src/examples/08-snapshot
fn commit_to(cluster : @raft.Cluster, max_ticks : Int32) -> Unit {
  match cluster.leader() {
    Some(l) => {
      let target = cluster.node(l).node().last_log_index()
      let _ = cluster.run_until_committed(target, max_ticks)
    }
    None => ()
  }
}

///|
fn main {
  let ids = ["a", "b", "c"]
  let cluster = @raft.Cluster::new(ids, seed=9)
  let lead = cluster.run_until_leader(200).unwrap()

  // Grow the log.
  for i in 0..<8 {
    let _ = cluster.propose(b"cmd-\{i}")
    commit_to(cluster, 100)
  }

  // Fold everything at or below index 5 into a snapshot.
  let _ = cluster.compact_leader(5, b"snapshot-payload")

  // While c is down, the leader keeps appending to its compacted log.
  cluster.crash("c")
  for i in 0..<4 {
    let _ = cluster.propose(b"more-\{i}")
    commit_to(cluster, 100)
  }
  let leader_committed = cluster.node(lead).commit_index()

  // c restarts and must catch up from the compacted log.
  cluster.restart("c")
  cluster.run(400)
  println(
    "c caught up to leader:   \{cluster.node("c").commit_index() == leader_committed}",
  )
  println("committed agree:         \{cluster.committed_agrees()}")
  println("invariants hold:         \{cluster.invariants_hold()}")
}