#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-only
set -euo pipefail

action="${1:-}"

log() { echo "[alma-creative-installer-helper] $*"; }

# -------------------------------------------------
# Repo enable actions (privileged)
# -------------------------------------------------
enable_epel() {
  log "Enabling EPEL..."
  dnf -y install epel-release
  log "Done."
}

enable_rpmfusion_free() {
  log "Enabling RPM Fusion Free..."
  local el_version
  el_version="$(rpm -E %rhel)"
  if [[ -z "${el_version}" || "${el_version}" == "%rhel" ]]; then
    echo "ERROR: Could not detect EL version." >&2
    exit 4
  fi
  dnf -y install "https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-${el_version}.noarch.rpm"
  log "RPM Fusion Free enabled."
}

enable_rpmfusion_nonfree() {
  log "Enabling RPM Fusion Free + Non-Free..."
  local el_version
  el_version="$(rpm -E %rhel)"
  if [[ -z "${el_version}" || "${el_version}" == "%rhel" ]]; then
    echo "ERROR: Could not detect EL version." >&2
    exit 4
  fi
  local pkgs=("https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-${el_version}.noarch.rpm")
  if ! dnf repolist --enabled 2>/dev/null | grep -q "^rpmfusion-free"; then
    pkgs=("https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-${el_version}.noarch.rpm" "${pkgs[@]}")
  fi
  dnf -y install "${pkgs[@]}"
  log "RPM Fusion Free + Non-Free enabled."
}

enable_nvidia_drivers() {
  log "Installing AlmaLinux native NVIDIA drivers..."
  if ! rpm -q almalinux-release >/dev/null 2>&1; then
    echo "ERROR: AlmaLinux native NVIDIA driver repo is only supported on AlmaLinux." >&2
    exit 6
  fi
  local el_version
  el_version="$(rpm -E %rhel)"
  if [[ -z "${el_version}" || "${el_version}" == "%rhel" || "${el_version}" -lt 9 ]]; then
    echo "ERROR: AlmaLinux native NVIDIA driver repo requires EL9 or newer (detected: EL${el_version})." >&2
    exit 6
  fi
  if ! lspci 2>/dev/null | grep -qi nvidia; then
    echo "ERROR: No NVIDIA GPU detected on this system. Aborting driver install." >&2
    exit 5
  fi
  dnf -y install almalinux-release-nvidia-driver
  log "NVIDIA driver repository configured."
  dnf -y install --allowerasing nvidia-open-kmod nvidia-driver
  log "NVIDIA drivers installed. A reboot is required to load the kernel module."
}

enable_crb() {
  log "Enabling CRB / PowerTools..."
  if dnf -y config-manager --set-enabled crb >/dev/null 2>&1; then
    log "Enabled: crb"
  elif dnf -y config-manager --set-enabled powertools >/dev/null 2>&1; then
    log "Enabled: powertools"
  else
    echo "ERROR: Could not enable CRB/powertools automatically." >&2
    exit 4
  fi
  log "Done."
}

repo_status() {
  log "Enabled repos:"
  dnf repolist --enabled
}

# -------------------------------------------------
# DNF packages
# -------------------------------------------------
install_pkg() {
  local pkg="${1:-}"
  [[ -n "${pkg}" ]] || { echo "ERROR: missing package name" >&2; exit 2; }

  # AppImageLauncher prerequisite: ensure FUSE runtime is present.
  # AppImages commonly require libfuse/libfuse2 compatibility on EL systems.
  if [[ "${pkg}" == *appimagelauncher* ]]; then
    log "Installing AppImageLauncher prerequisite: fuse/fuse-libs..."
    dnf -y install fuse fuse-libs || dnf -y install fuse-libs || dnf -y install fuse
    log "Prerequisite ready: fuse/fuse-libs"
  fi

  log "Installing: ${pkg}"
  dnf -y install "${pkg}"
  log "Done."
}

install_pkg_version() {
  local pkg_expr="${1:-}"
  [[ -n "${pkg_expr}" ]] || { echo "ERROR: missing package/version expression" >&2; exit 2; }
  log "Installing specific package version: ${pkg_expr}"
  dnf -y install "${pkg_expr}"
  log "Done."
}

remove_pkg() {
  local pkg="${1:-}"
  [[ -n "${pkg}" ]] || { echo "ERROR: missing package name" >&2; exit 2; }
  log "Removing: ${pkg}"
  dnf -y remove "${pkg}"
  log "Done."
}

pkg_status() {
  local pkg="${1:-}"
  [[ -n "${pkg}" ]] || { echo "ERROR: missing package name" >&2; exit 2; }
  if rpm -q "${pkg}" >/dev/null 2>&1; then
    log "Installed: ${pkg}"
    rpm -q "${pkg}"
  else
    log "Not installed: ${pkg}"
    exit 3
  fi
}

# -------------------------------------------------
# Flatpak / Krita support (system-wide)
# -------------------------------------------------
require_invoking_user() {
  if [[ -z "${PKEXEC_UID:-}" ]]; then
    echo "ERROR: PKEXEC_UID missing; cannot determine invoking user." >&2
    exit 6
  fi
  user="$(getent passwd "${PKEXEC_UID}" | cut -d: -f1 || true)"
  if [[ -z "${user}" ]]; then
    echo "ERROR: could not resolve invoking user for PKEXEC_UID=${PKEXEC_UID}" >&2
    exit 6
  fi
  echo "${user}"
}

require_invoking_user_home() {
  local user
  user="$(require_invoking_user)"
  local home
  home="$(getent passwd "${user}" | cut -d: -f6 || true)"
  if [[ -z "${home}" ]]; then
    echo "ERROR: could not resolve home directory for user ${user}" >&2
    exit 6
  fi
  echo "${home}"
}

run_flatpak_user() {
  local user home
  user="$(require_invoking_user)"
  home="$(require_invoking_user_home)"
  runuser -u "${user}" -- env HOME="${home}" flatpak --user "$@"
}

ensure_flatpak_flathub() {
  log "Ensuring Flatpak is installed..."
  dnf -y install flatpak

  log "Ensuring Flathub remote exists (system)..."
  flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo

  log "Flatpak + Flathub ready."
}

ensure_flatpak_flathub_user() {
  log "Ensuring Flatpak is installed..."
  dnf -y install flatpak

  log "Ensuring Flathub remote exists (user)..."
  run_flatpak_user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

  log "Flatpak + Flathub ready (user)."
}

install_flatpak_app() {
  local appid="${1:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  ensure_flatpak_flathub
  log "Installing Flatpak app: ${appid}"
  flatpak install -y --system flathub "${appid}"
  log "Done."
}

install_flatpak_app_branch() {
  local appid="${1:-}"
  local branch="${2:-stable}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  ensure_flatpak_flathub
  log "Installing Flatpak app: ${appid} (branch: ${branch})"
  flatpak install -y --system flathub "${appid}//${branch}"
  log "Done."
}

install_flatpak_app_commit() {
  local appid="${1:-}"
  local branch="${2:-stable}"
  local commit="${3:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  [[ -n "${commit}" ]] || { echo "ERROR: missing flatpak commit" >&2; exit 2; }
  ensure_flatpak_flathub
  log "Installing Flatpak app: ${appid} (branch: ${branch}, commit: ${commit})"
  flatpak install -y --system flathub "${appid}//${branch}"
  flatpak update -y --system --commit="${commit}" "${appid}"
  log "Done."
}

install_flatpak_app_user() {
  local appid="${1:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  ensure_flatpak_flathub_user
  log "Installing Flatpak app (user): ${appid}"
  run_flatpak_user install -y flathub "${appid}"
  log "Done."
}

install_flatpak_app_user_branch() {
  local appid="${1:-}"
  local branch="${2:-stable}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  ensure_flatpak_flathub_user
  log "Installing Flatpak app (user): ${appid} (branch: ${branch})"
  run_flatpak_user install -y flathub "${appid}//${branch}"
  log "Done."
}

install_flatpak_app_user_commit() {
  local appid="${1:-}"
  local branch="${2:-stable}"
  local commit="${3:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  [[ -n "${commit}" ]] || { echo "ERROR: missing flatpak commit" >&2; exit 2; }
  ensure_flatpak_flathub_user
  log "Installing Flatpak app (user): ${appid} (branch: ${branch}, commit: ${commit})"
  run_flatpak_user install -y flathub "${appid}//${branch}"
  run_flatpak_user update -y --commit="${commit}" "${appid}"
  log "Done."
}

remove_flatpak_app() {
  local appid="${1:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  log "Removing Flatpak app: ${appid}"
  flatpak uninstall -y --system "${appid}"
  log "Done."
}

remove_flatpak_app_user() {
  local appid="${1:-}"
  [[ -n "${appid}" ]] || { echo "ERROR: missing flatpak app id" >&2; exit 2; }
  log "Removing Flatpak app (user): ${appid}"
  run_flatpak_user uninstall -y "${appid}"
  log "Done."
}

# -------------------------------------------------
# DaVinci Resolve
# -------------------------------------------------
prepare_resolve_deps() {
  log "Installing DaVinci Resolve dependencies (baseline set)..."

  # Resolve installers/payloads on EL commonly require FUSE userspace runtime.
  # Keep this in Resolve deps (not only AppImageLauncher deps).
  log "Ensuring FUSE runtime for DaVinci Resolve..."
  dnf -y install fuse fuse-libs || dnf -y install fuse-libs || dnf -y install fuse || true

  dnf -y install \
    apr apr-util \
    libxcrypt-compat \
    ocl-icd \
    libXt \
    libX11 libXcursor libXext libXfixes libXi libXinerama libXrandr libXrender libXt libXtst \
    libxcb xcb-util xcb-util-image xcb-util-keysyms xcb-util-renderutil xcb-util-wm \
    libxkbcommon libxkbcommon-x11 \
    mesa-libGLU fontconfig freetype \
    libcurl --allowerasing \
    || exit 1

  # Resolve's package check still expects legacy "zlib" in some installer builds.
  # AlmaLinux 10 provides zlib-ng-compat; ensure one compatible provider exists.
  if ! rpm -q zlib >/dev/null 2>&1 && ! rpm -q zlib-ng-compat >/dev/null 2>&1; then
    log "Ensuring zlib compatibility package is present..."
    dnf -y install zlib || dnf -y install zlib-ng-compat || true
  fi

  log "Dependencies installed."
}

resolve_post_install_fixes() {
  local libs_dir="/opt/resolve/libs"
  local desktop_file="/usr/share/applications/com.blackmagicdesign.resolve.desktop"
  local icon_dest="/usr/share/icons/hicolor/256x256/apps/com.blackmagicdesign.resolve.png"
  local icon_src=""

  log "Applying DaVinci Resolve post-install compatibility fixes..."

  # 1) Prefer system GLib on newer EL releases by backing up bundled GLib libs.
  if [[ -d "${libs_dir}" ]]; then
    local glib_libs=(
      libglib-2.0.so libglib-2.0.so.0 libglib-2.0.so.0.6800.4
      libgobject-2.0.so libgobject-2.0.so.0 libgobject-2.0.so.0.6800.4
      libgio-2.0.so libgio-2.0.so.0 libgio-2.0.so.0.6800.4
      libgmodule-2.0.so libgmodule-2.0.so.0 libgmodule-2.0.so.0.6800.4
      libgthread-2.0.so libgthread-2.0.so.0
    )
    local lib
    for lib in "${glib_libs[@]}"; do
      if [[ -f "${libs_dir}/${lib}" && ! -f "${libs_dir}/${lib}.bak" ]]; then
        mv "${libs_dir}/${lib}" "${libs_dir}/${lib}.bak"
        log "Backed up bundled library: ${lib} -> ${lib}.bak"
      fi
    done
  else
    log "Resolve libs dir not found (${libs_dir}); skipping GLib compatibility step."
  fi

  # 2) Normalize desktop icon integration.
  if [[ -f "/opt/resolve/graphics/DV_Resolve.png" ]]; then
    icon_src="/opt/resolve/graphics/DV_Resolve.png"
  elif [[ -f "/opt/resolve/graphics/resolve.png" ]]; then
    icon_src="/opt/resolve/graphics/resolve.png"
  fi

  if [[ -n "${icon_src}" ]]; then
    install -Dm0644 "${icon_src}" "${icon_dest}"
    log "Installed Resolve icon to hicolor theme."
  else
    log "Resolve icon source not found under /opt/resolve/graphics; skipping icon copy."
  fi

  # 3) Keep desktop entry stable for icon and WMClass mapping.
  if [[ -f "${desktop_file}" ]]; then
    sed -i 's|^Icon=.*|Icon=com.blackmagicdesign.resolve|' "${desktop_file}"

    if grep -q '^StartupNotify=' "${desktop_file}"; then
      sed -i 's|^StartupNotify=.*|StartupNotify=true|' "${desktop_file}"
    else
      printf '\nStartupNotify=true\n' >> "${desktop_file}"
    fi

    if grep -q '^StartupWMClass=' "${desktop_file}"; then
      sed -i 's|^StartupWMClass=.*|StartupWMClass=resolve|' "${desktop_file}"
    else
      printf 'StartupWMClass=resolve\n' >> "${desktop_file}"
    fi

    log "Normalized desktop entry metadata (Icon + StartupWMClass)."
  else
    log "Desktop entry not found (${desktop_file}); skipping desktop metadata step."
  fi

  # 4) Refresh caches (best-effort).
  if command -v gtk-update-icon-cache >/dev/null 2>&1; then
    gtk-update-icon-cache -f /usr/share/icons/hicolor/ || true
  fi
  if command -v update-desktop-database >/dev/null 2>&1; then
    update-desktop-database /usr/share/applications || true
  fi

  log "Resolve post-install fixes completed."
}

set_selinux_mode() {
  local mode="${1:-}"
  [[ -n "${mode}" ]] || { echo "ERROR: missing selinux mode" >&2; exit 2; }

  case "${mode}" in
    permissive-temp)
      log "Setting SELinux to permissive (temporary)..."
      if command -v setenforce >/dev/null 2>&1; then
        setenforce 0 || true
      fi
      ;;
    permissive)
      log "Setting SELinux to permissive (permanent)..."
      sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
      if command -v setenforce >/dev/null 2>&1; then
        setenforce 0 || true
      fi
      ;;
    disabled)
      log "Setting SELinux to disabled (permanent)..."
      sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
      if command -v setenforce >/dev/null 2>&1; then
        setenforce 0 || true
      fi
      ;;
    enforcing)
      log "Setting SELinux to enforcing (permanent)..."
      sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
      if command -v setenforce >/dev/null 2>&1; then
        setenforce 1 || true
      fi
      ;;
    *)
      echo "ERROR: unknown selinux mode: ${mode}" >&2
      exit 2
      ;;
  esac

  log "SELinux change requested. A reboot may be required to fully apply."
}

install_local_file() {
  local path="${1:-}"
  if [[ -z "${path}" || ! -f "${path}" ]]; then
    echo "ERROR: installer path missing or not found." >&2
    exit 2
  fi

  case "${path}" in
    *.rpm)
      log "Installing local RPM via dnf: ${path}"
      dnf -y install "${path}"
      log "Done."
      ;;
    *.run)
      log "Running local .run installer: ${path}"
      chmod +x "${path}"
      if [[ "$(id -u)" -eq 0 ]]; then
        if [[ -n "${PKEXEC_UID:-}" ]]; then
          user="$(getent passwd "${PKEXEC_UID}" | cut -d: -f1 || true)"
          if [[ -z "${user}" ]]; then
            echo "ERROR: could not resolve invoking user for PKEXEC_UID=${PKEXEC_UID}" >&2
            exit 6
          fi
          log "Dropping privileges to run installer as ${user} (uid ${PKEXEC_UID})"
          runuser -u "${user}" -p -- env SKIP_PACKAGE_CHECK=1 "${path}"
        else
          echo "ERROR: .run installers must be run as a normal user (not root)." >&2
          exit 6
        fi
      else
        SKIP_PACKAGE_CHECK=1 "${path}"
      fi
      log "Installer finished (check output above)."
      ;;
    *)
      echo "ERROR: unsupported file type. Use .rpm or .run" >&2
      exit 5
      ;;
  esac
}

# -------------------------------------------------
# 3DCoat
# -------------------------------------------------
install_3dcoat() {
  local tar_path="${1:-}"
  local install_base="/opt/3DCoat"

  [[ -n "${tar_path}" ]] || { echo "ERROR: missing tarball path" >&2; exit 2; }
  [[ -f "${tar_path}" ]] || { echo "ERROR: tarball not found: ${tar_path}" >&2; exit 2; }

  local user home
  user="$(require_invoking_user)"
  home="$(require_invoking_user_home)"

  # Derive install dir from tarball filename (e.g. 3DCoat-2025.12.tar.bz2 -> 3DCoat-2025)
  local file_name soft soft_dir
  file_name="$(basename "${tar_path}")"
  soft="${file_name%%.*}"
  soft_dir="${install_base}/${soft}"

  log "Creating installation directory ${soft_dir}..."
  mkdir -p "${soft_dir}"

  log "Extracting archive (this may take several minutes)..."
  tar -xf "${tar_path}" -C "${soft_dir}" --strip-components=1
  log "Extraction complete."

  # Find the main executable (-print -quit avoids SIGPIPE with pipefail)
  local exe exe_name
  exe="$(find "${soft_dir}" -maxdepth 1 -type f -iname '3dcoat' -print -quit 2>/dev/null)"
  if [[ -z "${exe}" ]]; then
    exe="$(find "${soft_dir}" -maxdepth 1 -type f -iname '3dcoat*' -print -quit 2>/dev/null)"
  fi
  if [[ -z "${exe}" ]]; then
    echo "ERROR: 3DCoat executable not found in ${soft_dir}" >&2
    exit 1
  fi
  exe_name="$(basename "${exe}")"
  chmod +x "${exe}"
  log "Executable: ${exe}"

  # Create launcher shell script using the actual detected exe name
  local sh_path="${install_base}/3dcoat.sh"
  printf '#!/bin/bash\ncd "%s" && ./%s "$@"\n' "${soft_dir}" "${exe_name}" > "${sh_path}"
  chmod +x "${sh_path}"
  chown "${user}:${user}" "${sh_path}"

  # Symlink to /usr/bin
  [[ -L /usr/bin/3dcoat ]] && unlink /usr/bin/3dcoat || true
  ln -s "${sh_path}" /usr/bin/3dcoat
  log "Created /usr/bin/3dcoat -> ${sh_path}"

  # Desktop entry
  local icon_path="${soft_dir}/data/Icon/3DCoat.png"
  [[ -f "${icon_path}" ]] || icon_path="${sh_path}"

  local desktop_dir="${home}/.local/share/applications"
  mkdir -p "${desktop_dir}"
  chown "${user}:${user}" "${desktop_dir}"

  cat > "${desktop_dir}/3DCoat.desktop" <<EOF
[Desktop Entry]
Comment=3D modeling and sculpting software
Encoding=UTF-8
Version=1.0
Type=Application
Terminal=false
Exec=${sh_path}
Name=3DCoat
Icon=${icon_path}
Categories=Graphics;3DGraphics;
StartupWMClass=3dcoat
EOF
  chown "${user}:${user}" "${desktop_dir}/3DCoat.desktop"
  log "Desktop entry created."

  # Fix PNG color profiles (best-effort — suppresses rendering warnings)
  log "Installing ImageMagick for PNG profile normalization..."
  dnf -y install ImageMagick || true
  if command -v mogrify >/dev/null 2>&1; then
    local png_files=()
    while IFS= read -r f; do
      png_files+=("${f}")
    done < <(find "${soft_dir}" -type f -name '*.png' 2>/dev/null)
    local total="${#png_files[@]}"
    if (( total > 0 )); then
      log "Fixing PNG color profiles: ${total} files found..."
      local i=0
      for f in "${png_files[@]}"; do
        mogrify "${f}" 2>/dev/null || true
        i=$(( i + 1 ))
        if (( i % 100 == 0 || i == total )); then
          log "PNG fix: ${i}/${total} ($(( i * 100 / total ))%)"
        fi
      done
      log "PNG profile fix complete."
    else
      log "No PNG files found; skipping PNG color profile fix."
    fi
  else
    log "mogrify not available; skipping PNG color profile fix."
  fi

  log "3DCoat installation complete."
}

remove_3dcoat() {
  local user home
  user="$(require_invoking_user)"
  home="$(require_invoking_user_home)"

  log "Removing 3DCoat..."

  if [[ -L /usr/bin/3dcoat ]]; then
    unlink /usr/bin/3dcoat
    log "Removed /usr/bin/3dcoat symlink."
  fi

  if [[ -d /opt/3DCoat ]]; then
    rm -rf /opt/3DCoat
    log "Removed /opt/3DCoat."
  fi

  local desktop="${home}/.local/share/applications/3DCoat.desktop"
  if [[ -f "${desktop}" ]]; then
    rm -f "${desktop}"
    log "Removed desktop entry."
  fi

  log "3DCoat removed."
}

# -------------------------------------------------
# Dispatcher
# -------------------------------------------------
case "${action}" in
  # Repo
  enable_crb)             enable_crb;;
  enable_epel)            enable_epel;;
  enable_rpmfusion_free)    enable_rpmfusion_free;;
  enable_rpmfusion_nonfree) enable_rpmfusion_nonfree;;
  enable_nvidia_drivers)    enable_nvidia_drivers;;
  repo_status)              repo_status;;

  # DNF apps
  install_pkg)         install_pkg "${2:-}";;
  install_pkg_version) install_pkg_version "${2:-}";;
  remove_pkg)          remove_pkg "${2:-}";;
  pkg_status)          pkg_status "${2:-}";;

  # Flatpak
  ensure_flatpak_flathub) ensure_flatpak_flathub;;
  install_flatpak_app)    install_flatpak_app "${2:-}";;
  install_flatpak_app_branch) install_flatpak_app_branch "${2:-}" "${3:-stable}";;
  install_flatpak_app_commit) install_flatpak_app_commit "${2:-}" "${3:-stable}" "${4:-}";;
  install_flatpak_app_user) install_flatpak_app_user "${2:-}";;
  install_flatpak_app_user_branch) install_flatpak_app_user_branch "${2:-}" "${3:-stable}";;
  install_flatpak_app_user_commit) install_flatpak_app_user_commit "${2:-}" "${3:-stable}" "${4:-}";;
  remove_flatpak_app)     remove_flatpak_app "${2:-}";;
  remove_flatpak_app_user) remove_flatpak_app_user "${2:-}";;

  # Resolve
  prepare_resolve_deps)  prepare_resolve_deps;;
  resolve_post_install_fixes) resolve_post_install_fixes;;
  set_selinux_mode)      set_selinux_mode "${2:-}";;
  install_local_file)    install_local_file "${2:-}";;

  # 3DCoat
  install_3dcoat)        install_3dcoat "${2:-}";;
  remove_3dcoat)         remove_3dcoat;;

  *)
    cat >&2 <<'EOF'
Usage: almalinux-creative-installer-helper <action>

Repo actions:
  enable_crb
  enable_epel
  enable_rpmfusion_free
  enable_rpmfusion_nonfree
  enable_nvidia_drivers
  repo_status

DNF apps:
  install_pkg <name>
  install_pkg_version <name-version-release>
  remove_pkg <name>
  pkg_status <name>

Flatpak:
  ensure_flatpak_flathub
  install_flatpak_app <appid>
  install_flatpak_app_branch <appid> <branch>
  install_flatpak_app_commit <appid> <branch> <commit>
  install_flatpak_app_user <appid>
  install_flatpak_app_user_branch <appid> <branch>
  install_flatpak_app_user_commit <appid> <branch> <commit>
  remove_flatpak_app <appid>
  remove_flatpak_app_user <appid>

Local installers:
  prepare_resolve_deps
  resolve_post_install_fixes
  set_selinux_mode <permissive-temp|permissive|disabled|enforcing>
  install_local_file <path_to_.rpm_or_.run>

3DCoat:
  install_3dcoat <path_to_tarball>
  remove_3dcoat
EOF
    exit 1
    ;;
esac
