///|
#cfg(target="native")
let windows_icon_error_buffer_length = 1024

///|
#cfg(target="native")
#borrow(executable, icon, error_buffer)
extern "C" fn set_windows_executable_icon_ffi(
  executable : Bytes,
  executable_length : Int,
  icon : Bytes,
  icon_length : Int,
  error_buffer : FixedArray[Byte],
  error_buffer_length : Int,
) -> Int = "proton_package_set_windows_executable_icon"

///|
fn first_windows_icon(icons : Array[String]) -> String? {
  for icon in icons {
    if icon.to_lower().has_suffix(".ico") {
      return Some(icon)
    }
  }
  None
}

///|
#cfg(target="native")
async fn stage_windows_icon(spec : PackageSpec, executable : String) -> Unit {
  guard first_windows_icon(spec.icons) is Some(icon) else { return }
  guard path_is_file(icon) else {
    raise PackagePlanError::MissingIcon(path=icon)
  }
  let executable_bytes = @utf8.encode(executable)
  let icon_bytes = @utf8.encode(icon)
  let error_buffer = FixedArray::make(windows_icon_error_buffer_length, b'\x00')
  let status = set_windows_executable_icon_ffi(
    executable_bytes,
    executable_bytes.length(),
    icon_bytes,
    icon_bytes.length(),
    error_buffer,
    error_buffer.length(),
  )
  guard status == 0 else {
    let bytes = Bytes::from_array(error_buffer)
    let end = bytes.find(b"\x00").unwrap_or(bytes.length())
    let detail = @utf8.decode_lossy(bytes[0:end])
    raise WindowsPackagingError::ToolFailure(
      stage="embed Windows application icon",
      detail=if detail == "" {
        "native icon writer failed with status " + status.to_string()
      } else {
        detail
      },
    )
  }
}

///|
#cfg(target="wasm")
fn stage_windows_icon(
  spec : PackageSpec,
  _executable : String,
) -> Unit raise WindowsPackagingError {
  guard first_windows_icon(spec.icons) is Some(_) else { return }
  raise WindowsPackagingError::ToolFailure(
    stage="embed Windows application icon",
    detail="Windows icon embedding requires the native proton_package backend",
  )
}