flatpak_dir_uninstall (FlatpakDir                 *self,
                       FlatpakDecomposed          *ref,
                       FlatpakHelperUninstallFlags flags,
                       GCancellable               *cancellable,
                       GError                    **error)
{
  const char *repository;
  g_autoptr(FlatpakDecomposed) current_ref = NULL;
  gboolean was_deployed;
  g_autofree char *name = NULL;
  g_autofree char *old_active = NULL;
  g_auto(GLnxLockFile) lock = { 0, };
  g_autoptr(GBytes) deploy_data = NULL;
  gboolean keep_ref = flags & FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF;
  gboolean force_remove = flags & FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE;

  name = flatpak_decomposed_dup_id (ref);

  if (flatpak_dir_use_system_helper (self, NULL))
    {
      const char *installation = flatpak_dir_get_id (self);

      if (!flatpak_dir_system_helper_call_uninstall (self,
                                                     flags, flatpak_decomposed_get_ref (ref),
                                                     installation ? installation : "",
                                                     cancellable, error))
        return FALSE;

      return TRUE;
    }

  if (!flatpak_dir_lock (self, &lock,
                         cancellable, error))
    return FALSE;

  deploy_data = flatpak_dir_get_deploy_data (self, ref, FLATPAK_DEPLOY_VERSION_ANY,
                                             cancellable, error);
  if (deploy_data == NULL)
    return FALSE;

  /* Note: the origin remote usually exists but it's not guaranteed (the user
   * could have run remote-delete --force) */
  repository = flatpak_deploy_data_get_origin (deploy_data);
  if (repository == NULL)
    return FALSE;

  if (flatpak_decomposed_is_runtime (ref) && !force_remove)
    {
      g_autoptr(GPtrArray) blocking = NULL;

      /* Look for apps that need this runtime */
      blocking = flatpak_dir_list_app_refs_with_runtime (self, ref, cancellable, error);
      if (blocking == NULL)
        return FALSE;

      if (blocking->len > 0)
        {
          g_autoptr(GString) joined = g_string_new ("");
          for (int i = 0; i < blocking->len; i++)
            {
              FlatpakDecomposed *blocking_ref = g_ptr_array_index (blocking, i);
              g_autofree char *id = flatpak_decomposed_dup_id (blocking_ref);
              if (i != 0)
                g_string_append (joined, ", ");
              g_string_append (joined, id);
            }

          return flatpak_fail_error (error, FLATPAK_ERROR_RUNTIME_USED,
                                     _("Can't remove %s, it is needed for: %s"), flatpak_decomposed_get_pref (ref), joined->str);
        }
    }

  old_active = g_strdup (flatpak_deploy_data_get_commit (deploy_data));

  g_debug ("dropping active ref");
  if (!flatpak_dir_set_active (self, ref, NULL, cancellable, error))
    return FALSE;

  if (flatpak_decomposed_is_app (ref))
    {
      current_ref = flatpak_dir_current_ref (self, name, cancellable);
      if (current_ref != NULL &&
          flatpak_decomposed_equal (ref, current_ref))
        {
          g_debug ("dropping current ref");
          if (!flatpak_dir_drop_current_ref (self, name, cancellable, error))
            return FALSE;
        }
    }

  if (!flatpak_dir_update_deploy_ref (self, flatpak_decomposed_get_ref (ref), NULL, error))
    return FALSE;

  if (!flatpak_dir_undeploy_all (self, ref, force_remove, &was_deployed, cancellable, error))
    return FALSE;

  if (!keep_ref &&
      !flatpak_dir_remove_ref (self, repository, flatpak_decomposed_get_ref (ref), cancellable, error))
    return FALSE;

  /* Take this opportunity to clean up refs/mirrors/ since a prune will happen
   * after this uninstall operation. See
   * https://github.com/flatpak/flatpak/issues/3222
   */
  if (!flatpak_dir_delete_mirror_refs (self, FALSE, cancellable, error))
    return FALSE;

  if (flatpak_decomposed_is_app (ref) &&
      !flatpak_dir_update_exports (self, name, cancellable, error))
    return FALSE;

  glnx_release_lock_file (&lock);

  flatpak_dir_prune_origin_remote (self, repository);

  flatpak_dir_cleanup_removed (self, cancellable, NULL);

  if (!flatpak_dir_mark_changed (self, error))
    return FALSE;

  if (!was_deployed)
    {
      const char *branch = flatpak_decomposed_get_branch (ref);
      g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED,
                   _("%s branch %s is not installed"), name, branch);
      return FALSE;
    }

  flatpak_dir_log (self, "uninstall", NULL, flatpak_decomposed_get_ref (ref), NULL, old_active, NULL,
                   "Uninstalled %s", flatpak_decomposed_get_ref (ref));

  return TRUE;
}