// The entry points.

///|
/// Parse a `blocks` array.
///
/// Under `Lenient` -- the default -- this never fails: anything unmodelled
/// becomes an `Unknown` variant carrying its original JSON, which renders as
/// nothing but round-trips exactly. Under `Strict` the first unmodelled thing
/// raises, naming it.
///
/// A value that is not an array is not "no blocks", it is a caller mistake, so
/// it is `None` rather than an empty list.
pub fn parse_blocks(
  j : Json,
  policy? : UnknownPolicy = Lenient,
) -> Array[LayoutBlock]? raise BlockParseError {
  guard j is Array(items) else { return None }
  let blocks = items.map(LayoutBlock::from_json)
  if policy is Strict {
    for block in blocks {
      block.validate()
    }
  }
  Some(blocks)
}

///|
/// Parse the `blocks` field of a message, view or payload object.
///
/// `None` when there is no `blocks` field at all, which is different from an
/// empty one -- see the `blocks: []` case in @api's form encoder.
pub fn parse_message_blocks(
  message : Json,
  policy? : UnknownPolicy = Lenient,
) -> Array[LayoutBlock]? raise BlockParseError {
  guard message is Object(o) else { return None }
  guard o.get("blocks") is Some(blocks) else { return None }
  parse_blocks(blocks, policy~)
}

///|
/// Serialise a block list, ready for `Params::put_json("blocks", ...)`.
pub fn blocks_to_json(blocks : Array[LayoutBlock]) -> Json {
  Json::array(blocks.map(LayoutBlock::to_json))
}

///|
/// Raise on the first unmodelled thing in a block list.
pub fn validate_blocks(
  blocks : Array[LayoutBlock],
) -> Unit raise BlockParseError {
  for block in blocks {
    block.validate()
  }
}

///|
/// Every block type name this version models. For tests, and for anyone
/// checking what a payload will survive.
pub let known_block_types : Array[String] = [
  "actions", "call", "context", "divider", "file", "header", "image", "input", "markdown",
  "rich_text", "section", "video",
]

///|
/// Every element type name this version models.
pub let known_element_types : Array[String] = [
  "button", "channels_select", "checkboxes", "conversations_select", "datepicker",
  "datetimepicker", "external_select", "image", "mrkdwn", "multi_channels_select",
  "multi_conversations_select", "multi_external_select", "multi_static_select", "multi_users_select",
  "overflow", "plain_text", "plain_text_input", "radio_buttons", "rich_text_input",
  "static_select", "timepicker", "users_select", "workflow_button",
]