///|
/// A value's ABI representation in its defining type environment. Flat output
/// may be bounded by the caller's ABI arity limit; memory layout never expands
/// fixed-length lists. An absent layout means the value exceeds the supported
/// addressable area, while its bounded call signature can still be inspected.
pub(all) struct CanonicalAbiPlan {
  value_type : ValType
  types : Array[TypeDef?]
  memory64 : Bool
  flat : Array[@types.ValueType]
  layout : (Int, Int)?
  contains_memory : Bool
}

///|
pub fn canonical_abi_plan(
  ty : ValType,
  types : Array[TypeDef?],
  memory64 : Bool,
  max_flat? : Int = 2147483647,
) -> CanonicalAbiPlan? {
  guard canonical_flat_types(
      ty,
      types,
      if memory64 {
        I64
      } else {
        I32
      },
      max_flat~,
    )
    is Some(flat) else {
    return None
  }
  Some({
    value_type: ty,
    types,
    memory64,
    flat,
    layout: canonical_memory_layout(ty, types, memory64),
    contains_memory: canonical_value_contains_memory(ty, types),
  })
}

///|
pub fn canonical_value_contains_memory(
  ty : ValType,
  types : Array[TypeDef?],
) -> Bool {
  let visited : Map[Int, Unit] = Map([])
  fn contains(ty : ValType) -> Bool {
    match ty {
      Prim(String) => true
      Prim(_) => false
      TypeIdx(idx) => {
        if visited.get(idx) is Some(_) {
          return false
        }
        visited.set(idx, ())
        match types.get(idx) {
          Some(Some(DefValType(p))) => contains(Prim(p))
          Some(Some(List(_))) => true
          Some(Some(FixedList(element, _))) | Some(Some(Option(element))) =>
            contains(element)
          Some(Some(Tuple(items))) => items.any(contains)
          Some(Some(Record(fields))) => fields.any(field => contains(field.ty))
          Some(Some(Variant(cases))) =>
            cases.any(case => case.ty is Some(ty) && contains(ty))
          Some(Some(Result(ok, err))) =>
            (ok is Some(ty) && contains(ty)) ||
            (err is Some(ty) && contains(ty))
          _ => false
        }
      }
    }
  }
  contains(ty)
}