///|
/// Minimal bank-withdrawal demo showing transactional rollback.
///
/// Three scenes: a withdrawal blocked by insufficient funds, a withdrawal
/// that writes first and is then rejected by a policy rule (the write is
/// rolled back — the balance never dips), and a legal withdrawal that
/// commits. Run with `moon run examples/rollback`.
fn main raise {
  let balance = @transaction.TVar::new(100)
  let min_required = 50

  // Scene 1: overdraft. `check` raises Retry, the body aborts, and nothing
  // is written — the caller sees the balance untouched.
  @transaction.atomically(txn => {
    let funds = txn.read(balance)
    txn.check(funds >= 500)
    txn.write(balance, funds - 500)
  }) catch {
    err => {
      assert_true(@transaction.is_retry(err))
      println("withdraw 500: blocked (insufficient funds)")
    }
  }
  println("  balance: \{balance.peek()}")

  // Scene 2: the write happens, then a policy rule rejects it. The whole
  // transaction rolls back — the deduction never becomes visible, even
  // though the body already performed it.
  @transaction.atomically(txn => {
    let funds = txn.read(balance)
    txn.check(funds >= 60)
    txn.write(balance, funds - 60)
    // Business rule: the account must always keep at least 50.
    if funds - 60 < min_required {
      (fail("would drop below the minimum balance of 50") : Unit)
    }
  }) catch {
    err => {
      assert_true(!@transaction.is_retry(err))
      println("withdraw 60: rejected and rolled back")
      println("  reason: would drop below the minimum balance of 50")
      ignore(err)
    }
  }
  println("  balance: \{balance.peek()}")

  // Scene 3: a legal withdrawal commits as one atomic step.
  @transaction.atomically(txn => {
    let funds = txn.read(balance)
    txn.check(funds - 30 >= min_required)
    txn.write(balance, funds - 30)
  })
  println("withdraw 30: committed")
  println("  balance: \{balance.peek()}")
}