///|
/// Validates invariants before using state loaded from external storage.
pub fn validate_state(state : LeaseState) -> Array[StateIssue] {
  let issues : Array[StateIssue] = []
  if state.resource == "" {
    issues.push({
      code: "empty_resource",
      message: "resource must not be empty",
    })
  }
  if state.next_token <= 0 {
    issues.push({
      code: "invalid_next_token",
      message: "next token must be positive",
    })
  }
  if state.revision < 0 {
    issues.push({
      code: "invalid_revision",
      message: "revision must not be negative",
    })
  }
  match state.lease {
    Some(lease) => {
      if lease.resource != state.resource {
        issues.push({
          code: "resource_mismatch",
          message: "nested lease resource differs from state resource",
        })
      }
      if lease.holder == "" {
        issues.push({
          code: "empty_holder",
          message: "lease holder must not be empty",
        })
      }
      if lease.token <= 0 {
        issues.push({
          code: "invalid_token",
          message: "lease token must be positive",
        })
      }
      if lease.token >= state.next_token {
        issues.push({
          code: "token_cursor_regression",
          message: "next token must be greater than issued token",
        })
      }
      if lease.revision <= 0 || lease.revision > state.revision {
        issues.push({
          code: "lease_revision_mismatch",
          message: "lease revision must be positive and not exceed state revision",
        })
      }
      if lease.issued_at < 0 || lease.expires_at <= lease.issued_at {
        issues.push({
          code: "invalid_interval",
          message: "lease interval must be positive and non-negative",
        })
      }
    }
    None => ()
  }
  issues
}

///|
/// Returns whether restored state is safe to apply.
pub fn state_is_valid(state : LeaseState) -> Bool {
  validate_state(state).length() == 0
}