///|
pub(all) suberror WiteError {
  UnexpectedEof
  InvalidMagic
  UnsupportedVersion(String)
  InvalidFormat(String)
}

///|
fn wite_error_to_string(err : WiteError) -> String {
  match err {
    WiteError::UnexpectedEof => "unexpected eof"
    WiteError::InvalidMagic => "invalid wasm magic"
    WiteError::UnsupportedVersion(msg) => "unsupported version: " + msg
    WiteError::InvalidFormat(msg) => "invalid format: " + msg
  }
}

///|
pub fn error_to_string(err : WiteError) -> String {
  wite_error_to_string(err)
}

///|
pub(all) struct SectionSize {
  key : String
  section_id : UInt
  total_bytes : UInt
}

///|
pub(all) struct OptimizeConfig {
  strip_all_custom : Bool
  strip_name_section : Bool
  strip_producers_section : Bool
  strip_debug_sections : Bool
  strip_dwarf_sections : Bool
  strip_target_features_section : Bool
  keep_custom_sections : Array[String]
  pass_rounds : UInt
  enable_peephole : Bool
  peephole_remove_nop : Bool
  peephole_remove_const_drop : Bool
  enable_vacuum : Bool
  enable_merge_blocks : Bool
  enable_remove_unused_brs : Bool
  enable_dce : Bool
  enable_dfe : Bool
  enable_merge_similar_functions : Bool
  enable_remove_unused_module_elements : Bool
  enable_experimental_cfp_const_hints : Bool
  closed_world : Bool
  closed_world_root_exports : Array[String]
  safe_mode : Bool
}

///|
pub fn OptimizeConfig::default() -> Self {
  {
    strip_all_custom: false,
    strip_name_section: true,
    strip_producers_section: true,
    strip_debug_sections: false,
    strip_dwarf_sections: false,
    strip_target_features_section: false,
    keep_custom_sections: [],
    pass_rounds: 1U,
    enable_peephole: true,
    peephole_remove_nop: true,
    peephole_remove_const_drop: true,
    enable_vacuum: true,
    enable_merge_blocks: true,
    enable_remove_unused_brs: true,
    enable_dce: false,
    enable_dfe: false,
    enable_merge_similar_functions: false,
    enable_remove_unused_module_elements: false,
    enable_experimental_cfp_const_hints: false,
    closed_world: false,
    closed_world_root_exports: [],
    safe_mode: false,
  }
}

///|
pub fn OptimizeConfig::aggressive() -> Self {
  {
    strip_all_custom: true,
    strip_name_section: true,
    strip_producers_section: true,
    strip_debug_sections: true,
    strip_dwarf_sections: true,
    strip_target_features_section: true,
    keep_custom_sections: [],
    pass_rounds: 1U,
    enable_peephole: true,
    peephole_remove_nop: true,
    peephole_remove_const_drop: true,
    enable_vacuum: true,
    enable_merge_blocks: true,
    enable_remove_unused_brs: true,
    enable_dce: false,
    enable_dfe: false,
    enable_merge_similar_functions: false,
    enable_remove_unused_module_elements: false,
    enable_experimental_cfp_const_hints: false,
    closed_world: false,
    closed_world_root_exports: [],
    safe_mode: false,
  }
}

///|
pub fn OptimizeConfig::o0() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=false,
    strip_dwarf_sections=false,
    strip_target_features_section=false,
    keep_custom_sections=[],
    pass_rounds=1U,
    enable_peephole=false,
    peephole_remove_nop=false,
    peephole_remove_const_drop=false,
    enable_vacuum=false,
    enable_merge_blocks=false,
    enable_remove_unused_brs=false,
    enable_dce=false,
    enable_dfe=false,
    enable_merge_similar_functions=false,
  )
}

///|
/// Shrink-first preset.
/// By default this strips debug/dwarf/target-features metadata in addition to code optimizations.
pub fn OptimizeConfig::o1() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=true,
    strip_dwarf_sections=true,
    strip_target_features_section=true,
    keep_custom_sections=[],
    pass_rounds=2U,
    enable_peephole=true,
    peephole_remove_nop=true,
    peephole_remove_const_drop=true,
    enable_vacuum=true,
    enable_merge_blocks=true,
    enable_remove_unused_brs=true,
    enable_dce=true,
    enable_dfe=true,
    enable_merge_similar_functions=true,
    enable_remove_unused_module_elements=true,
  )
}

///|
/// Throughput/balance presets.
/// By default these keep debug/custom metadata unless explicit strip flags are enabled by CLI.
pub fn OptimizeConfig::o2() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=false,
    strip_dwarf_sections=false,
    strip_target_features_section=false,
    keep_custom_sections=[],
    pass_rounds=2U,
    enable_peephole=true,
    peephole_remove_nop=true,
    peephole_remove_const_drop=true,
    enable_vacuum=true,
    enable_merge_blocks=true,
    enable_remove_unused_brs=true,
    enable_dce=true,
    enable_dfe=false,
    enable_merge_similar_functions=false,
  )
}

///|
pub fn OptimizeConfig::o3() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=false,
    strip_dwarf_sections=false,
    strip_target_features_section=false,
    keep_custom_sections=[],
    pass_rounds=3U,
    enable_peephole=true,
    peephole_remove_nop=true,
    peephole_remove_const_drop=true,
    enable_vacuum=true,
    enable_merge_blocks=true,
    enable_remove_unused_brs=true,
    enable_dce=true,
    enable_dfe=true,
    enable_merge_similar_functions=true,
    enable_remove_unused_module_elements=true,
  )
}

///|
pub fn OptimizeConfig::os() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=false,
    strip_dwarf_sections=false,
    strip_target_features_section=false,
    keep_custom_sections=[],
    pass_rounds=3U,
    enable_peephole=true,
    peephole_remove_nop=true,
    peephole_remove_const_drop=true,
    enable_vacuum=true,
    enable_merge_blocks=true,
    enable_remove_unused_brs=true,
    enable_dce=true,
    enable_dfe=true,
    enable_merge_similar_functions=false,
    enable_remove_unused_module_elements=true,
  )
}

///|
pub fn OptimizeConfig::oz() -> Self {
  make_optimize_config(
    strip_all_custom=false,
    strip_name_section=false,
    strip_producers_section=false,
    strip_debug_sections=false,
    strip_dwarf_sections=false,
    strip_target_features_section=false,
    keep_custom_sections=[],
    pass_rounds=8U,
    enable_peephole=true,
    peephole_remove_nop=true,
    peephole_remove_const_drop=true,
    enable_vacuum=true,
    enable_merge_blocks=true,
    enable_remove_unused_brs=true,
    enable_dce=true,
    enable_dfe=true,
    enable_merge_similar_functions=true,
    enable_remove_unused_module_elements=true,
  )
}

///|
pub fn optimize_config_from_opt_level(level : String) -> OptimizeConfig? {
  match level {
    "-O0" => Some(OptimizeConfig::o0())
    "-O1" => Some(OptimizeConfig::o1())
    "-O" | "-O2" => Some(OptimizeConfig::o2())
    "-O3" | "-O4" => Some(OptimizeConfig::o3())
    "-Os" => Some(OptimizeConfig::os())
    "-Oz" => Some(OptimizeConfig::oz())
    _ => None
  }
}

///|
pub fn make_optimize_config(
  strip_all_custom? : Bool = false,
  strip_name_section? : Bool = true,
  strip_producers_section? : Bool = true,
  strip_debug_sections? : Bool = false,
  strip_dwarf_sections? : Bool = false,
  strip_target_features_section? : Bool = false,
  keep_custom_sections? : Array[String] = [],
  pass_rounds? : UInt = 1U,
  enable_peephole? : Bool = true,
  peephole_remove_nop? : Bool = true,
  peephole_remove_const_drop? : Bool = true,
  enable_vacuum? : Bool = true,
  enable_merge_blocks? : Bool = true,
  enable_remove_unused_brs? : Bool = true,
  enable_dce? : Bool = false,
  enable_dfe? : Bool = false,
  enable_merge_similar_functions? : Bool = false,
  enable_remove_unused_module_elements? : Bool = false,
  enable_experimental_cfp_const_hints? : Bool = false,
  closed_world? : Bool = false,
  closed_world_root_exports? : Array[String] = [],
  safe_mode? : Bool = false,
) -> OptimizeConfig {
  {
    strip_all_custom,
    strip_name_section,
    strip_producers_section,
    strip_debug_sections,
    strip_dwarf_sections,
    strip_target_features_section,
    keep_custom_sections,
    pass_rounds,
    enable_peephole,
    peephole_remove_nop,
    peephole_remove_const_drop,
    enable_vacuum,
    enable_merge_blocks,
    enable_remove_unused_brs,
    enable_dce,
    enable_dfe,
    enable_merge_similar_functions,
    enable_remove_unused_module_elements,
    enable_experimental_cfp_const_hints,
    closed_world,
    closed_world_root_exports,
    safe_mode,
  }
}

///|
pub(all) struct OptimizeResult {
  bytes : Bytes
  before_size : UInt
  after_size : UInt
  removed_sections : Array[String]
  observations : Array[String]
  no_change_reasons : Array[String]
}

///|
pub(all) struct ModuleProfile {
  total_bytes : UInt
  function_count : UInt
  import_count : UInt
  export_count : UInt
  code_body_count : UInt
  code_body_bytes : UInt
  sections : Array[SectionSize]
}

///|
pub(all) struct FunctionSize {
  function_index : UInt
  body_bytes : UInt
  name : String?
  export_names : Array[String]
}

///|
pub(all) struct FunctionGapEntry {
  match_kind : String
  match_key : String
  left_function_index : UInt?
  right_function_index : UInt?
  left_name : String?
  right_name : String?
  left_export_names : Array[String]
  right_export_names : Array[String]
  left_body_bytes : UInt
  right_body_bytes : UInt
  left_minus_right_bytes : Int
  abs_gap_bytes : UInt
}

///|
pub(all) struct FunctionGapReport {
  left_total_body_bytes : UInt
  right_total_body_bytes : UInt
  left_minus_right_total_bytes : Int
  unmatched_left_count : UInt
  unmatched_right_count : UInt
  entries : Array[FunctionGapEntry]
}

///|
pub(all) struct CodeBlockSize {
  function_index : UInt
  function_name : String?
  export_names : Array[String]
  block_index : UInt
  depth : UInt
  kind : String
  start_offset : UInt
  end_offset : UInt
  instruction_count : UInt
  total_bytes : UInt
}

///|
pub(all) struct CodeBlockSizeReport {
  partial : Bool
  function_count : UInt
  block_count : UInt
  total_body_bytes : UInt
  total_instruction_bytes : UInt
  blocks : Array[CodeBlockSize]
}

///|
pub(all) struct CustomSectionBreakdown {
  name : String
  count : UInt
  total_bytes : UInt
  payload_bytes : UInt
  content_bytes : UInt
}

///|
pub(all) struct OpcodeStat {
  key : String
  mnemonic : String
  count : UInt
  total_bytes : UInt
}

///|
pub(all) struct WasmBreakdownReport {
  total_bytes : UInt
  sections : Array[SectionSize]
  custom_sections : Array[CustomSectionBreakdown]
  imported_function_count : UInt
  local_function_count : UInt
  top_functions : Array[FunctionSize]
  block_count : UInt
  top_blocks : Array[CodeBlockSize]
  instruction_count : UInt
  instruction_bytes : UInt
  opcode_partial : Bool
  opcodes : Array[OpcodeStat]
  callgraph_partial : Bool
  has_indirect_calls : Bool
  reachable_body_bytes : UInt
  dead_body_bytes : UInt
}

///|
pub(all) struct CallGraphNode {
  function_index : UInt
  body_bytes : UInt
  name : String?
  export_names : Array[String]
  direct_callees : Array[UInt]
  reachable_from_roots : Bool
}

///|
pub(all) struct CallGraphReport {
  imported_function_count : UInt
  local_function_count : UInt
  roots : Array[UInt]
  has_indirect_calls : Bool
  partial : Bool
  reachable_body_bytes : UInt
  dead_body_bytes : UInt
  nodes : Array[CallGraphNode]
}

///|
pub(all) struct CallGraphSummary {
  imported_function_count : UInt
  local_function_count : UInt
  roots : Array[UInt]
  has_indirect_calls : Bool
  partial : Bool
  reachable_function_count : UInt
  dead_function_count : UInt
  reachable_body_bytes : UInt
  dead_body_bytes : UInt
}

///|
pub(all) struct HostCodeHint {
  kind : String
  function_index : UInt
  function_name : String?
  target_function_index : UInt
  target_function_name : String?
  export_names : Array[String]
  appended_i32_const_count : UInt
  dropped_param_count : UInt
}

///|
pub(all) struct CfpConstHintRejectReasonCount {
  reason : String
  count : UInt
}

///|
pub(all) struct HostGeneratedCodeReport {
  imported_function_count : UInt
  local_function_count : UInt
  param_forwarding_thunk_count : UInt
  const_forwarding_thunk_count : UInt
  signature_refinable_thunk_count : UInt
  cfp_const_hint_section_count : UInt
  cfp_const_hint_unknown_version_section_count : UInt
  cfp_const_hint_malformed_section_count : UInt
  cfp_const_hint_entry_count : UInt
  cfp_const_hint_usable_entry_count : UInt
  cfp_const_hint_reject_reason_counts : Array[CfpConstHintRejectReasonCount]
  cfp_const_specialize_hint_candidate_count : UInt
  cfp_const_specialize_hint_reject_reason_counts : Array[
    CfpConstHintRejectReasonCount,
  ]
  directize_candidate_call_count : UInt
  dce_removable_function_count : UInt
  dce_removable_body_bytes : UInt
  dce_partial : Bool
  hints : Array[HostCodeHint]
}

///|
pub(all) struct FunctionSizeDiff {
  function_index : UInt
  before_body_bytes : UInt
  after_body_bytes : UInt
  gain_bytes : UInt
  regression_bytes : UInt
  before_name : String?
  after_name : String?
  export_names : Array[String]
}

///|
pub(all) struct OptimizeStageMetadata {
  stage : String
  before_size : UInt
  after_size : UInt
  gain_bytes : UInt
  regression_bytes : UInt
  function_gain_bytes : UInt
  function_regression_bytes : UInt
  function_diffs : Array[FunctionSizeDiff]
  removed_sections : Array[String]
  observations : Array[String]
  no_change_reasons : Array[String]
}

///|
pub(all) struct OptimizeMetadataReport {
  before_size : UInt
  after_size : UInt
  total_gain_bytes : UInt
  total_regression_bytes : UInt
  stages : Array[OptimizeStageMetadata]
}

///|
pub(all) struct DceReport {
  roots : Array[UInt]
  partial : Bool
  removable_function_count : UInt
  removable_body_bytes : UInt
  removable_functions : Array[FunctionSize]
}

///|
pub(all) struct ComponentProfile {
  total_bytes : UInt
  import_count : UInt
  export_count : UInt
  core_module_count : UInt
  nested_component_count : UInt
  sections : Array[SectionSize]
  core_modules : Array[ModuleProfile]
}

///|
pub(all) struct ComponentFunctionSizeReport {
  module_index : UInt
  function_count : UInt
  total_body_bytes : UInt
  functions : Array[FunctionSize]
}

///|
pub(all) struct ComponentCallGraphReport {
  module_index : UInt
  graph : CallGraphReport
}

///|
pub(all) struct ComponentCoreOptimizeEntry {
  module_index : UInt
  before_bytes : UInt
  after_bytes : UInt
  removed_sections : Array[String]
  no_change_reasons : Array[String]
}

///|
pub(all) struct ComponentCoreOptimizeReport {
  total_component_bytes : UInt
  core_module_count : UInt
  total_core_before_bytes : UInt
  total_core_after_bytes : UInt
  entries : Array[ComponentCoreOptimizeEntry]
}

///|
pub(all) struct ContractReport {
  component_imports : Array[String]
  component_exports : Array[String]
  wit_imports : Array[String]
  wit_exports : Array[String]
  missing_component_imports : Array[String]
  missing_component_exports : Array[String]
}

///|
pub(all) struct KeepReasonEntry {
  function_index : UInt
  name : String?
  export_names : Array[String]
  reasons : Array[String]
}

///|
pub(all) struct KeepReasonReport {
  partial : Bool
  entries : Array[KeepReasonEntry]
}

///|
pub(all) struct RetainPathEntry {
  function_index : UInt
  name : String?
  export_names : Array[String]
  body_bytes : UInt
  root_reasons : Array[String]
  path : Array[UInt]
}

///|
pub(all) struct RetainPathReport {
  partial : Bool
  roots : Array[UInt]
  entries : Array[RetainPathEntry]
}

///|
pub(all) struct HotnessSizeEntry {
  export_name : String
  function_index : UInt?
  function_name : String?
  body_bytes : UInt
  calls : UInt
  total_ns : UInt64
  average_ns : UInt64
  bucket : String
  unresolved_reason : String?
  unresolved_detail : String?
}

///|
pub(all) struct HotnessSizeBucket {
  bucket : String
  count : UInt
  body_bytes : UInt
  total_ns : UInt64
}

///|
pub(all) struct HotnessUnresolvedReasonCount {
  reason : String
  count : UInt
}

///|
pub(all) struct HotnessSizeMatrixReport {
  iterations : UInt
  instantiate_ns : UInt64
  hot_threshold_ns : UInt64
  large_threshold_bytes : UInt
  unresolved_exports : Array[String]
  unresolved_reason_counts : Array[HotnessUnresolvedReasonCount]
  buckets : Array[HotnessSizeBucket]
  entries : Array[HotnessSizeEntry]
}

///|
pub(all) struct ComponentRootPolicyReport {
  component_imports : Array[String]
  component_exports : Array[String]
  wit_imports : Array[String]
  wit_exports : Array[String]
  canonical_abi_roots : Array[String]
  root_name_candidates : Array[String]
}

///|
pub(all) struct RuntimeFunctionProfile {
  name : String
  calls : UInt
  total_ns : UInt64
  average_ns : UInt64
}

///|
pub(all) struct RuntimeUnresolvedExport {
  export_name : String
  reason : String
  detail : String
}

///|
pub(all) struct RuntimeProfileScenario {
  export_name : String
  args : Array[Int]
}

///|
pub fn make_runtime_profile_scenario(
  export_name : String,
  args? : Array[Int] = [],
) -> RuntimeProfileScenario {
  { export_name, args }
}

///|
pub(all) struct RuntimeProfile {
  instantiate_ns : UInt64
  iterations : UInt
  functions : Array[RuntimeFunctionProfile]
  skipped_exports : Array[String]
  unresolved_exports : Array[RuntimeUnresolvedExport]
}