///|
fn gen_valid_reorder_globals_profile_config(
  profile : GenValidProfile,
) -> GenValidConfig {
  let base = GenValidConfig::binaryen_oracle_coverage_forced_default()
  let uses_imports = profile == ReorderGlobalsImportedProfile ||
    profile == ReorderGlobalsModuleCodeProfile
  let profile_tag = match profile {
    ReorderGlobalsHotTrafficProfile => 90
    ReorderGlobalsDependencySearchProfile => 91
    ReorderGlobalsImportedProfile => 92
    ReorderGlobalsModuleCodeProfile => 93
    ReorderGlobalsLegacyEhProfile => 94
    ReorderGlobalsMetadataRemapProfile => 95
    ReorderGlobalsThresholdProfile => 96
    _ => 90
  }
  GenValidConfig::{
    ..base,
    max_type_defs: 2,
    max_imports: if uses_imports {
      129
    } else {
      0
    },
    max_defined_funcs: 1,
    max_tables: if profile == ReorderGlobalsModuleCodeProfile {
      1
    } else {
      0
    },
    max_mems: if profile == ReorderGlobalsModuleCodeProfile {
      1
    } else {
      0
    },
    max_globals: if uses_imports {
      0
    } else {
      129
    },
    max_tags: if profile == ReorderGlobalsLegacyEhProfile {
      1
    } else {
      0
    },
    max_elems: if profile == ReorderGlobalsModuleCodeProfile {
      1
    } else {
      0
    },
    max_datas: if profile == ReorderGlobalsModuleCodeProfile {
      1
    } else {
      0
    },
    max_params: 0,
    max_results: 1,
    max_locals: 0,
    max_body_statements: profile_tag,
    max_body_depth: 4,
    features: GenValidFeatureToggles::{
      ..base.features,
      allow_imports: uses_imports,
      allow_tables: profile == ReorderGlobalsModuleCodeProfile,
      allow_mems: profile == ReorderGlobalsModuleCodeProfile,
      allow_globals: true,
      allow_tags: profile == ReorderGlobalsLegacyEhProfile,
      allow_elems: profile == ReorderGlobalsModuleCodeProfile,
      allow_datas: profile == ReorderGlobalsModuleCodeProfile,
      allow_ref_types: profile == ReorderGlobalsModuleCodeProfile,
      allow_calls: false,
      allow_call_indirect: false,
      allow_tail_calls: false,
      allow_branch_heavy_control: profile == ReorderGlobalsLegacyEhProfile,
      allow_v128: false,
      allow_relaxed_simd: false,
      allow_atomics: false,
      allow_shared_memories: false,
      allow_memory64: false,
      allow_subtyping_topology: false,
      allow_name_custom_sections: profile == ReorderGlobalsMetadataRemapProfile,
      allow_typed_body_generator: false,
      allow_ssa_nomerge_body_generator: false,
      allow_heap_store_optimization_body_generator: false,
      allow_heap2local_body_generator: false,
    },
  }
}

///|
fn gen_valid_is_reorder_globals_profile_config(config : GenValidConfig) -> Bool {
  config == GenValidConfig::for_profile(ReorderGlobalsHotTrafficProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsDependencySearchProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsImportedProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsModuleCodeProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsLegacyEhProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsMetadataRemapProfile) ||
  config == GenValidConfig::for_profile(ReorderGlobalsThresholdProfile)
}

///|
pub fn gen_valid_reorder_globals_profile_case_label(
  profile : GenValidProfile,
  seed : UInt64,
) -> String? {
  match profile {
    ReorderGlobalsHotTrafficProfile =>
      if (seed & 1UL) == 0UL {
        Some("reorder-globals:function-global-get-traffic")
      } else {
        Some("reorder-globals:function-global-set-traffic")
      }
    ReorderGlobalsDependencySearchProfile =>
      match (seed % 4UL).to_int() {
        0 => Some("reorder-globals:dependency-original-order-win")
        1 => Some("reorder-globals:dependency-summed-count-win")
        2 => Some("reorder-globals:dependency-greedy-win")
        _ => Some("reorder-globals:dependency-branching-dag")
      }
    ReorderGlobalsImportedProfile =>
      Some("reorder-globals:imported-global-sort")
    ReorderGlobalsModuleCodeProfile =>
      match (seed % 4UL).to_int() {
        0 => Some("reorder-globals:element-offset-traffic")
        1 => Some("reorder-globals:data-offset-traffic")
        2 => Some("reorder-globals:table-initializer-traffic")
        _ => Some("reorder-globals:element-item-traffic")
      }
    ReorderGlobalsLegacyEhProfile =>
      match (seed % 5UL).to_int() {
        0 => Some("reorder-globals:legacy-eh-protected")
        1 => Some("reorder-globals:legacy-eh-typed-catch")
        2 => Some("reorder-globals:legacy-eh-catch-all")
        3 => Some("reorder-globals:legacy-eh-delegate")
        _ => Some("reorder-globals:try-table-protected")
      }
    ReorderGlobalsMetadataRemapProfile =>
      Some("reorder-globals:export-and-name-remap")
    ReorderGlobalsThresholdProfile =>
      match (seed % 3UL).to_int() {
        0 => Some("reorder-globals:127-global-public-noop")
        1 => Some("reorder-globals:128-global-cost-tie")
        _ => Some("reorder-globals:129-global-uleb-win")
      }
    _ => None
  }
}

///|
fn gen_valid_rg_void_type() -> RecType {
  RecType::new(SubType::comp_type(CompType::func([], [])))
}

///|
fn gen_valid_rg_i32_result_type() -> RecType {
  RecType::new(SubType::comp_type(CompType::func([], [ValType::i32()])))
}

///|
fn gen_valid_rg_i32_global(value : Int32, mutable_~ : Bool = false) -> Global {
  Global::new(
    GlobalType::new(ValType::i32(), mutable_),
    Expr::new([Instruction::i32_const(I32(value))]),
  )
}

///|
fn gen_valid_rg_i32_globals(
  count : Int32,
  mutable_~ : Bool = false,
) -> Array[Global] {
  let globals : Array[Global] = Array::new(capacity=count)
  for idx = 0; idx < count; idx = idx + 1 {
    globals.push(gen_valid_rg_i32_global(idx, mutable_~))
  }
  globals
}

///|
fn gen_valid_rg_drop_gets(
  global_idx : UInt32,
  repetitions : Int32,
) -> Array[Instruction] {
  let body : Array[Instruction] = []
  for rep = 0; rep < repetitions; rep = rep + 1 {
    ignore(rep)
    body.push(Instruction::global_get(GlobalIdx::new(global_idx)))
    body.push(Instruction::drop())
  }
  body
}

///|
fn gen_valid_rg_void_module(
  globals : Array[Global],
  body : Array[Instruction],
) -> Module {
  Module::new()
  .with_type_sec(TypeSec::new([gen_valid_rg_void_type()]))
  .with_global_sec(GlobalSec::new(globals))
  .with_func_sec(FuncSec::new([TypeIdx::new(0U)]))
  .with_code_sec(CodeSec::new([Func::new(Locals::empty(), Expr::new(body))]))
}

///|
fn gen_valid_rg_hot_traffic_module(seed : UInt64) -> Module {
  if (seed & 1UL) == 0UL {
    gen_valid_rg_void_module(
      gen_valid_rg_i32_globals(129),
      gen_valid_rg_drop_gets(128U, 1 + ((seed >> 8) % 7UL).to_int()),
    )
  } else {
    let body : Array[Instruction] = []
    let repetitions = 2 + ((seed >> 8) % 5UL).to_int()
    for rep = 0; rep < repetitions; rep = rep + 1 {
      body.push(Instruction::i32_const(I32(rep)))
      body.push(Instruction::global_set(GlobalIdx::new(128U)))
    }
    body.push(Instruction::global_get(GlobalIdx::new(0U)))
    body.push(Instruction::drop())
    gen_valid_rg_void_module(gen_valid_rg_i32_globals(129, mutable_=true), body)
  }
}

///|
fn gen_valid_rg_dependency_chain_globals(
  other_position : Int32,
) -> Array[Global] {
  let globals : Array[Global] = Array::new(capacity=129)
  let mut chain_old_idx = 0
  for idx = 0; idx < 129; idx = idx + 1 {
    if idx == other_position {
      globals.push(gen_valid_rg_i32_global(9000))
    } else if chain_old_idx == 0 {
      globals.push(gen_valid_rg_i32_global(1))
      chain_old_idx += 1
    } else {
      let previous_abs = idx - 1
      let dependency = if previous_abs == other_position {
        previous_abs - 1
      } else {
        previous_abs
      }
      globals.push(
        Global::new(
          GlobalType::new(ValType::i32(), false),
          Expr::new([
            Instruction::global_get(
              GlobalIdx::new(dependency.reinterpret_as_uint()),
            ),
          ]),
        ),
      )
      chain_old_idx += 1
    }
  }
  globals
}

///|
fn gen_valid_rg_dependency_search_module(seed : UInt64) -> Module {
  match (seed % 4UL).to_int() {
    0 => {
      let globals = gen_valid_rg_dependency_chain_globals(128)
      let body = gen_valid_rg_drop_gets(127U, 10)
      for instr in gen_valid_rg_drop_gets(128U, 2) {
        body.push(instr)
      }
      gen_valid_rg_void_module(globals, body)
    }
    1 => {
      let globals = gen_valid_rg_dependency_chain_globals(2)
      let body = gen_valid_rg_drop_gets(128U, 10)
      for instr in gen_valid_rg_drop_gets(2U, 2) {
        body.push(instr)
      }
      gen_valid_rg_void_module(globals, body)
    }
    2 =>
      gen_valid_rg_void_module(
        gen_valid_rg_dependency_chain_globals(128),
        gen_valid_rg_drop_gets(128U, 4),
      )
    _ => {
      let globals = gen_valid_rg_i32_globals(129)
      globals[126] = Global::new(
        GlobalType::new(ValType::i32(), false),
        Expr::new([Instruction::global_get(GlobalIdx::new(0U))]),
      )
      globals[127] = Global::new(
        GlobalType::new(ValType::i32(), false),
        Expr::new([Instruction::global_get(GlobalIdx::new(1U))]),
      )
      globals[128] = Global::new(
        GlobalType::new(ValType::i32(), false),
        Expr::new([
          Instruction::global_get(GlobalIdx::new(126U)),
          Instruction::global_get(GlobalIdx::new(127U)),
          Instruction::i32_add(),
        ]),
      )
      gen_valid_rg_void_module(globals, gen_valid_rg_drop_gets(128U, 8))
    }
  }
}

///|
fn gen_valid_rg_imported_module(seed : UInt64) -> Module {
  let imports : Array[Import] = Array::new(capacity=129)
  for idx = 0; idx < 129; idx = idx + 1 {
    imports.push(
      Import::new(
        Name::from_string("rg"),
        Name::from_string("g\{idx}-\{(seed >> 8) & 0xffUL}"),
        ExternType::global(GlobalType::new(ValType::i32(), false)),
      ),
    )
  }
  Module::new()
  .with_type_sec(TypeSec::new([gen_valid_rg_i32_result_type()]))
  .with_import_sec(ImportSec::new(imports))
  .with_func_sec(FuncSec::new([TypeIdx::new(0U)]))
  .with_export_sec(
    ExportSec::new([
      Export::new(Name::from_string("run"), ExternIdx::func(FuncIdx::new(0U))),
    ]),
  )
  .with_code_sec(
    CodeSec::new([
      Func::new(
        Locals::empty(),
        Expr::new([Instruction::global_get(GlobalIdx::new(128U))]),
      ),
    ]),
  )
}

///|
fn gen_valid_rg_global_imports(
  count : Int32,
  value_type : ValType,
) -> Array[Import] {
  let imports : Array[Import] = Array::new(capacity=count)
  for idx = 0; idx < count; idx = idx + 1 {
    imports.push(
      Import::new(
        Name::from_string("rg-module-code"),
        Name::from_string("g\{idx}"),
        ExternType::global(GlobalType::new(value_type, false)),
      ),
    )
  }
  imports
}

///|
fn gen_valid_rg_module_code_module(seed : UInt64) -> Module {
  let variant = (seed % 4UL).to_int()
  let funcref = RefType::new(true, HeapType::abs(AbsHeapType::func()))
  let mut mod_ = Module::new()
    .with_type_sec(TypeSec::new([gen_valid_rg_void_type()]))
    .with_func_sec(FuncSec::new([TypeIdx::new(0U)]))
    .with_code_sec(
      CodeSec::new([Func::new(Locals::empty(), Expr::new([Instruction::nop()]))]),
    )
  mod_ = mod_.with_import_sec(
    ImportSec::new(
      gen_valid_rg_global_imports(
        129,
        if variant <= 1 {
          ValType::i32()
        } else {
          ValType::funcref()
        },
      ),
    ),
  )
  match variant {
    0 =>
      mod_
      .with_table_sec(
        TableSec::new([
          Table::new(TableType::new(funcref, Limits::i32(1U, None)), None),
        ]),
      )
      .with_elem_sec(
        ElemSec::new([
          Elem::new(
            ElemMode::active(
              TableIdx::new(0U),
              Expr::new([Instruction::global_get(GlobalIdx::new(128U))]),
            ),
            ElemKind::funcs([FuncIdx::new(0U)]),
          ),
        ]),
      )
    1 =>
      mod_
      .with_mem_sec(MemSec::new([MemType::new(Limits::i32(1U, None))]))
      .with_data_sec(
        DataSec::new([
          Data::new(
            DataMode::active(
              MemIdx::new(0U),
              Expr::new([Instruction::global_get(GlobalIdx::new(128U))]),
            ),
            Bytes::from_array([0x52]),
          ),
        ]),
      )
    2 =>
      mod_.with_table_sec(
        TableSec::new([
          Table::new(
            TableType::new(funcref, Limits::i32(1U, None)),
            Some(Expr::new([Instruction::global_get(GlobalIdx::new(128U))])),
          ),
        ]),
      )
    _ =>
      mod_.with_elem_sec(
        ElemSec::new([
          Elem::new(
            ElemMode::passive(),
            ElemKind::typed_exprs(funcref, [
              Expr::new([Instruction::global_get(GlobalIdx::new(128U))]),
            ]),
          ),
        ]),
      )
  }
}

///|
fn gen_valid_rg_legacy_eh_module(seed : UInt64) -> Module {
  let tag = TagIdx::new(0U)
  let hot = Expr::new(gen_valid_rg_drop_gets(128U, 4))
  let body = match (seed % 5UL).to_int() {
    0 =>
      [
        Instruction::try_(BlockType::void_(), hot, [
          @lib.LegacyCatch::tagged(tag, Expr::new([Instruction::nop()])),
        ]),
      ]
    1 =>
      [
        Instruction::try_(
          BlockType::void_(),
          Expr::new([Instruction::throw_(tag)]),
          [@lib.LegacyCatch::tagged(tag, hot)],
        ),
      ]
    2 =>
      [
        Instruction::try_(
          BlockType::void_(),
          Expr::new([Instruction::throw_(tag)]),
          [@lib.LegacyCatch::all(hot)],
        ),
      ]
    3 =>
      [
        Instruction::try_(
          BlockType::void_(),
          Expr::new([
            Instruction::try_(
              BlockType::void_(),
              hot,
              [],
              delegate=Some(LabelIdx::new(0U)),
            ),
          ]),
          [@lib.LegacyCatch::tagged(tag, Expr::new([Instruction::nop()]))],
        ),
      ]
    _ =>
      [
        Instruction::block(
          BlockType::void_(),
          Expr::new([
            Instruction::try_table(
              BlockType::void_(),
              [Catch::new(tag, LabelIdx::new(0U))],
              hot,
            ),
          ]),
        ),
      ]
  }
  gen_valid_rg_void_module(gen_valid_rg_i32_globals(129), body).with_tag_sec(
    TagSec::new([TagType::new(TypeIdx::new(0U))]),
  )
}

///|
fn gen_valid_rg_metadata_module(seed : UInt64) -> Module {
  let salt = ((seed >> 8) & 0xffUL).to_int()
  gen_valid_rg_void_module(
    gen_valid_rg_i32_globals(129),
    gen_valid_rg_drop_gets(128U, 3 + salt % 4),
  )
  .with_export_sec(
    ExportSec::new([
      Export::new(
        Name::from_string("hot"),
        ExternIdx::global(GlobalIdx::new(128U)),
      ),
    ]),
  )
  .with_name_sec(
    NameSec::new(
      global_names=Some(
        NameMap::new([
          NameAssoc::new(0U, Name::from_string("cold")),
          NameAssoc::new(128U, Name::from_string("hot")),
        ]),
      ),
    ),
  )
}

///|
fn gen_valid_rg_threshold_module(seed : UInt64) -> Module {
  let count = match (seed % 3UL).to_int() {
    0 => 127
    1 => 128
    _ => 129
  }
  gen_valid_rg_void_module(
    gen_valid_rg_i32_globals(count),
    gen_valid_rg_drop_gets((count - 1).reinterpret_as_uint(), 4),
  )
}

///|
fn gen_valid_reorder_globals_profile_module(ctx : GenValidContext) -> Module {
  let config = ctx.config
  if config ==
    GenValidConfig::for_profile(ReorderGlobalsDependencySearchProfile) {
    gen_valid_rg_dependency_search_module(ctx.root_seed)
  } else if config == GenValidConfig::for_profile(ReorderGlobalsImportedProfile) {
    gen_valid_rg_imported_module(ctx.root_seed)
  } else if config ==
    GenValidConfig::for_profile(ReorderGlobalsModuleCodeProfile) {
    gen_valid_rg_module_code_module(ctx.root_seed)
  } else if config == GenValidConfig::for_profile(ReorderGlobalsLegacyEhProfile) {
    gen_valid_rg_legacy_eh_module(ctx.root_seed)
  } else if config ==
    GenValidConfig::for_profile(ReorderGlobalsMetadataRemapProfile) {
    gen_valid_rg_metadata_module(ctx.root_seed)
  } else if config ==
    GenValidConfig::for_profile(ReorderGlobalsThresholdProfile) {
    gen_valid_rg_threshold_module(ctx.root_seed)
  } else {
    gen_valid_rg_hot_traffic_module(ctx.root_seed)
  }
}