///|
pub fn resolve_aliases(value : Yaml) -> Yaml raise ParseError {
  let anchors : Map[String, Yaml] = {}
  resolve_node(value, anchors)
}

///|
fn resolve_node(
  value : Yaml,
  anchors : Map[String, Yaml],
) -> Yaml raise ParseError {
  match value {
    Alias(name) =>
      match anchors.get(name) {
        Some(v) => v
        None => raise ParseError::UnknownAnchor(name~)
      }
    Anchor(name~, value~) => {
      let resolved = resolve_node(value, anchors)
      anchors[name] = resolved
      Yaml::Anchor(name~, value=resolved)
    }
    Tagged(tag~, value~) =>
      Yaml::Tagged(tag~, value=resolve_node(value, anchors))
    Seq(items) => Yaml::Seq(items.map(item => resolve_node(item, anchors)))
    Map(items) => resolve_map(items, anchors)
    _ => value
  }
}

///|
fn resolve_map(
  items : Array[(Yaml, Yaml)],
  anchors : Map[String, Yaml],
) -> Yaml raise ParseError {
  let resolved : Array[(Yaml, Yaml)] = items.map(pair => {
    let (k, v) = pair
    (resolve_node(k, anchors), resolve_node(v, anchors))
  })
  Yaml::Map(merge_entries(resolved))
}

///|
fn merge_entries(
  items : Array[(Yaml, Yaml)],
) -> Array[(Yaml, Yaml)] raise ParseError {
  let result : Array[(Yaml, Yaml)] = []
  for pair in items {
    let (key, value) = pair
    if is_merge_key(key) {
      merge_value(value, result)
    } else {
      upsert_entry(result, key, value)
    }
  }
  result
}

///|
fn merge_value(
  value : Yaml,
  result : Array[(Yaml, Yaml)],
) -> Unit raise ParseError {
  match value {
    Map(items) => merge_map_items(items, result)
    Seq(items) =>
      for item in items {
        match item {
          Map(map_items) => merge_map_items(map_items, result)
          _ =>
            raise ParseError::InvalidMerge(
              detail="merge sequence must contain maps",
            )
        }
      }
    _ =>
      raise ParseError::InvalidMerge(
        detail="merge value must be map or sequence",
      )
  }
}

///|
fn merge_map_items(
  items : Array[(Yaml, Yaml)],
  result : Array[(Yaml, Yaml)],
) -> Unit {
  for pair in items {
    let (key, value) = pair
    if find_key_index(result, key) is None {
      result.push((key, value))
    }
  }
}

///|
fn upsert_entry(result : Array[(Yaml, Yaml)], key : Yaml, value : Yaml) -> Unit {
  match find_key_index(result, key) {
    None => result.push((key, value))
    Some(index) => result[index] = (key, value)
  }
}

///|
fn find_key_index(items : Array[(Yaml, Yaml)], key : Yaml) -> Int32? {
  for i in 0.. Bool {
  match key {
    String("<<") => true
    Tagged(tag~, value~) => is_merge_tag(tag) || value is String("<<")
    _ => false
  }
}

///|
fn is_merge_tag(tag : String) -> Bool {
  tag is "!!merge" ||
  tag is "!" ||
  tag is "tag:yaml.org,2002:merge"
}