///|
fn example_value(
  model : @smithy.Model,
  id : @smithy.ShapeId,
  value : @smithy.NodeValue,
  config : Config,
  depth : Int32,
) -> @smithy.NodeValue raise OpenRpcError {
  if depth > 128 {
    raise InvalidContract(id, "I reached the modeled example nesting limit.")
  }
  guard model.shapes.get(id) is Some(shape) else {
    raise InvalidContract(id, "I could not resolve the example data shape.")
  }
  let target = match shape.body {
    Member(target~) => model.shapes[target]
    _ => shape
  }
  match (target.body, value) {
    (Structure(_) | Union(_), Object(values)) => {
      let out : Map[String, @smithy.NodeValue] = Map([])
      for name, value in values {
        if target.id.with_member(name) is Some(member_id) &&
          model.shapes.get(member_id) is Some(member_) {
          out[member_name(member_, config)] = example_value(
            model,
            member_id,
            value,
            config,
            depth + 1,
          )
        } else {
          raise InvalidContract(
            id,
            "I found unknown modeled example member `" +
            name +
            "` on `" +
            target.id.to_string() +
            "`.",
          )
        }
      }
      @smithy.Object(out)
    }
    (List(member_), Array(values)) =>
      @smithy.Array(
        values.map(v => example_value(model, member_, v, config, depth + 1)),
      )
    (Map(value=member_, ..), Object(values)) => {
      let out : Map[String, @smithy.NodeValue] = Map([])
      for key, value in values {
        out[key] = example_value(model, member_, value, config, depth + 1)
      }
      @smithy.Object(out)
    }
    _ => value
  }
}

///|
fn[T] example_error(
  id : @smithy.ShapeId,
  title : String,
  message : String,
) -> T raise OpenRpcError {
  raise InvalidContract(
    id,
    "I could not export example `" + title + "`: " + message,
  )
}

///|
fn validate_example_result(
  prepared : @rpc.PreparedService,
  contract : @rpc.MethodContract,
  response : Map[String, @smithy.NodeValue],
  title : String,
) -> Unit raise OpenRpcError {
  response["jsonrpc"] = @smithy.String("2.0")
  response["id"] = @smithy.Null
  let checked = prepared.validate_response(
    @smithy.Object(response).to_json().stringify(),
    context=Some(@rpc.Method(contract.name)),
  )
  if !checked.is_valid() {
    example_error(
      contract.operation,
      title,
      checked.findings.map(f => f.message).join(" "),
    )
  }
}

///|
/// Standard example pairings and the `x-smithy-error-examples` entries.
priv struct MethodExamples {
  successes : Array[@rpcdoc.ExamplePairing]
  errors : Array[Json]
}

///|
fn method_examples(
  model : @smithy.Model,
  config : Config,
  prepared : @rpc.PreparedService,
  contract : @rpc.MethodContract,
  warnings : Array[ConversionWarning],
) -> MethodExamples raise OpenRpcError {
  let successes : Array[@rpcdoc.ExamplePairing] = []
  let errors : Array[Json] = []
  let operation = model.shapes[contract.operation]
  guard operation.trait_value("smithy.api#examples")
    is Some(@smithy.Array(examples)) else {
    return { successes, errors, }
  }
  let titles : Set[String] = Set([])
  for example in examples {
    guard example is @smithy.Object(fields) &&
      fields.get("title") is Some(@smithy.String(title)) &&
      !title.is_empty() else {
      example_error(
        contract.operation,
        "",
        "I expected a nonempty example title.",
      )
    }
    if titles.contains(title) {
      example_error(
        contract.operation,
        title,
        "I expected unique example names.",
      )
    }
    titles.add(title)
    let input = fields.get("input").unwrap_or(@smithy.Object(Map([])))
    let wire = example_value(model, contract.input, input, config, 0)
    guard wire is @smithy.Object(values) else {
      example_error(contract.operation, title, "I expected a structure input.")
    }
    let ids = parameter_members(model, contract, config)
    let params : Array[@rpcdoc.InlineOrReference[@rpcdoc.Example]] = []
    let positional : Array[@smithy.NodeValue] = []
    let mut hole = false
    let mut interior = false
    for id in ids {
      let name = member_name(model.shapes[id], config)
      match values.get(name) {
        Some(value) => {
          if hole {
            interior = true
          }
          positional.push(value)
          params.push(
            literal_example(name, value.to_json(), contract.operation),
          )
        }
        None => hole = true
      }
    }
    if contract.mode == @rpc.Positional && interior {
      example_error(
        contract.operation,
        title,
        "I cannot omit an interior positional argument; provide an explicit valid value.",
      )
    }
    let request = @smithy.Object({
      "jsonrpc": @smithy.String("2.0"),
      "method": @smithy.String(contract.name.to_string()),
      "params": if contract.mode == @rpc.Positional {
        @smithy.Array(positional)
      } else {
        wire
      },
    })
    let checked = prepared.validate_request(request.to_json().stringify())
    if !checked.is_valid() {
      example_error(
        contract.operation,
        title,
        checked.findings.map(f => f.message).join(" "),
      )
    }
    let description = match fields.get("documentation") {
      Some(@smithy.String(text)) => Some(text)
      _ => None
    }
    let pairing_extensions : Array[(String, Json)] = []
    if interior && contract.mode == @rpc.Either {
      if config.extensions == Preserve {
        pairing_extensions.push(
          ("x-smithy-param-structure", Json::string("by-name")),
        )
      } else {
        if config.extensions == Fail {
          raise UnsupportedMapping(
            contract.operation,
            "I need x-smithy-param-structure for a named example omitting interior positional arguments.",
          )
        }
        warnings.push({
          code: OmittedExample,
          shape: contract.operation,
          message: "I omitted example `" +
          title +
          "` because it needs named parameter metadata.",
        })
        continue
      }
    }
    if fields.get("error") is Some(@smithy.Object(error)) {
      guard error.get("shapeId") is Some(@smithy.String(shape_id)) &&
        @smithy.ShapeId::parse(shape_id) is Some(error_id) else {
        example_error(
          contract.operation,
          title,
          "I expected a modeled application error shape.",
        )
      }
      let mut selected : @rpc.ErrorContract? = None
      for error in contract.errors {
        if error.shape == error_id {
          selected = Some(error)
        }
      }
      guard selected is Some(mapping) else {
        example_error(
          contract.operation,
          title,
          "I expected a modeled application error shape.",
        )
      }
      let content = error.get("content").unwrap_or(@smithy.Object(Map([])))
      let wire = example_value(model, error_id, content, config, 0)
      let error_value : Map[String, @smithy.NodeValue] = {
        "code": @smithy.Number(mapping.code.number()),
        "message": @smithy.String(error_id.name()),
      }
      match mapping.data {
        None => ()
        Some(target) if target.member_name() is Some(name) => {
          let member_ = model.shapes[target]
          if wire is @smithy.Object(content) &&
            content.get(member_name(member_, config)) is Some(value) {
            error_value["data"] = value
          } else if mapping.data_required {
            example_error(
              contract.operation,
              title,
              "I expected required error data member `" + name + "`.",
            )
          }
        }
        Some(_) => error_value["data"] = wire
      }
      validate_example_result(
        prepared,
        contract,
        { "error": @smithy.Object(error_value) },
        title,
      )
      match config.extensions {
        Preserve => {
          let pairing = built(
            @rpcdoc.ExamplePairing::new(
              title,
              params,
              description~,
              extensions=extensions(pairing_extensions, contract.operation),
              additional_fields=[
                (
                  "error",
                  exact(
                    @smithy.Object(error_value).to_json(),
                    contract.operation,
                  ),
                ),
              ],
              limits=limits(),
            ),
            contract.operation,
          )
          errors.push(
            built(
              @exact.to_core_json(pairing.to_json(), limits=limits()),
              contract.operation,
            ),
          )
        }
        Omit =>
          warnings.push({
            code: OmittedExample,
            shape: contract.operation,
            message: "I omitted error example `" +
            title +
            "`; OpenRPC example pairings have no standard error field.",
          })
        Fail =>
          raise UnsupportedMapping(
            contract.operation,
            "I need x-smithy-error-examples for modeled application error examples.",
          )
      }
    } else {
      let mut result = None
      if fields.get("output") is Some(output) {
        let value = match contract.result {
          @rpc.NullResult => @smithy.Null
          @rpc.OutputStructure(target) =>
            example_value(model, target, output, config, 0)
          @rpc.OutputMember(target) => {
            guard output is @smithy.Object(values) &&
              values.get(target.member_name().unwrap()) is Some(value) else {
              example_error(
                contract.operation,
                title,
                "I expected the selected result member in the example output.",
              )
            }
            example_value(model, target, value, config, 0)
          }
        }
        validate_example_result(prepared, contract, { "result": value }, title)
        result = Some(
          literal_example("result", value.to_json(), contract.operation),
        )
      }
      successes.push(
        built(
          @rpcdoc.ExamplePairing::new(
            title,
            params,
            description~,
            result~,
            extensions=extensions(pairing_extensions, contract.operation),
            limits=limits(),
          ),
          contract.operation,
        ),
      )
    }
  }
  { successes, errors, }
}