///|
/// A small end-to-end example for DynamoDB Local at 127.0.0.1:8000.
async fn main {
  let config : @lib.Config = {
    endpoint: "http://127.0.0.1:8000",
    region: "us-east-1",
    credentials: {
      access_key_id: "local",
      secret_access_key: "local",
      session_token: None,
    },
  }
  let db = @lib.Client::new(config)
  try db.create_table("MoonDynamoExample", "pk") |> ignore catch {
    @lib.DynamoError::Service(_, "ResourceInUseException", _, _) => ()
    error => raise error
  }
  try db.create_table("MoonDynamoExampleArchive", "pk") |> ignore catch {
    @lib.DynamoError::Service(_, "ResourceInUseException", _, _) => ()
    error => raise error
  }
  db.describe_table("MoonDynamoExample") |> ignore
  let tables = db.list_tables(limit=1)
  guard !tables.table_names.is_empty() else {
    fail("ListTables returned no tables")
  }
  let all_tables = db.list_all_tables(limit=1)
  let mut found_main = false
  let mut found_archive = false
  for name in all_tables {
    if name == "MoonDynamoExample" {
      found_main = true
    }
    if name == "MoonDynamoExampleArchive" {
      found_archive = true
    }
  }
  guard found_main && found_archive else {
    fail("list_all_tables missed a page")
  }
  // "status" collides with a DynamoDB reserved word in expressions, so the
  // query below reaches it through a #name placeholder.
  let item : @lib.Item = Map([
    ("pk", @lib.Text("book#1")),
    ("title", @lib.Text("The MoonBit Book")),
    ("pages", @lib.Number("128")),
    ("status", @lib.Text("active")),
  ])
  db.put_item("MoonDynamoExample", item)
  let key : @lib.Item = Map([("pk", @lib.Text("book#1"))])
  let found = db.get_item("MoonDynamoExample", key)
  guard found is Some(found_item) else { fail("GetItem returned no item") }
  guard found_item.get("title") == Some(@lib.Text("The MoonBit Book")) else {
    fail("GetItem returned an unexpected item")
  }
  let selected = db.get_item(
    "MoonDynamoExample",
    key,
    projection_expression="title, #st",
    expression_attribute_names=Map([("#st", "status")]),
  )
  guard selected is Some(selected_item) else {
    fail("projected GetItem returned no item")
  }
  guard selected_item.get("title") == Some(@lib.Text("The MoonBit Book")) &&
    selected_item.get("status") == Some(@lib.Text("active")) &&
    selected_item.get("pages") is None else {
    fail("projected GetItem returned unexpected fields")
  }
  let batch = db.batch_get_item(
    "MoonDynamoExample",
    [key],
    consistent_read=true,
    projection_expression="pk, title, #st",
    expression_attribute_names=Map([("#st", "status")]),
  )
  guard batch.items.length() == 1 &&
    batch.unprocessed_keys.is_empty() &&
    batch.items[0].get("pk") == Some(@lib.Text("book#1")) &&
    batch.items[0].get("status") == Some(@lib.Text("active")) &&
    batch.items[0].get("pages") is None else {
    fail("projected BatchGetItem returned unexpected fields")
  }
  let updated = db.update_item(
    "MoonDynamoExample",
    key,
    "SET pages = :pages",
    expression_attribute_values=Map([
      (":pages", @lib.Number("200")),
      (":expected", @lib.Number("128")),
    ]),
    condition_expression="pages = :expected",
    return_values=AllNew,
  )
  guard updated is Some(book) && book.get("pages") == Some(@lib.Number("200")) else {
    fail("UpdateItem did not apply the new page count")
  }
  try
    db.update_item(
      "MoonDynamoExample",
      key,
      "SET pages = :pages",
      expression_attribute_values=Map([
        (":pages", @lib.Number("999")),
        (":expected", @lib.Number("128")),
      ]),
      condition_expression="pages = :expected",
    )
    |> ignore
  catch {
    @lib.DynamoError::Service(400, "ConditionalCheckFailedException", _, _) =>
      ()
    error => raise error
  }
  let after_rejected = db.get_item("MoonDynamoExample", key)
  guard after_rejected is Some(book) &&
    book.get("pages") == Some(@lib.Number("200")) else {
    fail("failed conditional update changed the item")
  }
  let result = db.query(
    "MoonDynamoExample",
    "pk = :pk",
    Map([(":pk", @lib.Text("book#1")), (":st", @lib.Text("active"))]),
    filter_expression="#st = :st",
    expression_attribute_names=Map([("#st", "status")]),
    projection_expression="title",
  )
  guard result.items.length() == 1 &&
    result.items[0].get("title") == Some(@lib.Text("The MoonBit Book")) &&
    result.items[0].get("pages") is None else {
    fail("Query projection returned unexpected fields")
  }
  let scanned = db.scan(
    "MoonDynamoExample",
    projection_expression="title, #st",
    expression_attribute_names=Map([("#st", "status")]),
  )
  guard scanned.items.length() == 1 &&
    scanned.items[0].get("status") == Some(@lib.Text("active")) &&
    scanned.items[0].get("pages") is None else {
    fail("Scan projection returned unexpected fields")
  }
  db.delete_item("MoonDynamoExample", key)
  guard db.get_item("MoonDynamoExample", key) is None else {
    fail("DeleteItem did not remove the item")
  }
  println("MoonDynamo local example passed")
}