# Utilities not in nixpkgs.
plutil="/usr/bin/plutil"
killall="/usr/bin/killall"
osacompile="/usr/bin/osacompile"

copyable_app_props=(
  "CFBundleDevelopmentRegion"
  "CFBundleDocumentTypes"
  "CFBundleGetInfoString"
  "CFBundleIconFile"
  "CFBundleIdentifier"
  "CFBundleInfoDictionaryVersion"
  "CFBundleName"
  "CFBundleShortVersionString"
  "CFBundleURLTypes"
  "NSAppleEventsUsageDescription"
  "NSAppleScriptEnabled"
  "NSDesktopFolderUsageDescription"
  "NSDocumentsFolderUsageDescription"
  "NSDownloadsFolderUsageDescription"
  "NSPrincipalClass"
  "NSRemovableVolumesUsageDescription"
  "NSServices"
  "UTExportedTypeDeclarations"
)

function sync_icons() {
  local from="$1"
  local to="$2"
  from_resources="$from/Contents/Resources/"
  to_resources="$to/Contents/Resources/"

  find "$to_resources" -name "*.icns" -delete
  rsync --include "*.icns" --exclude "*" --recursive "$from_resources" "$to_resources"
}

function copy_paths() {
  local from="$1"
  local to="$2"
  local paths=("${@:3}")

  keys=$(jq -n '$ARGS.positional' --args "${paths[@]}")
  jqfilter="to_entries |[.[]| select(.key as \$item| \$keys | index(\$item) >= 0) ] | from_entries"

  temp_dir=$(mktemp -d)
  trap 'rm -rf "$temp_dir"' EXIT

  pushd $temp_dir >/dev/null

  cp "$from" "orig"
  chmod u+w "orig"

  cp "$to" "bare-wrapper"
  chmod u+w "bare-wrapper"

  $plutil -convert json -- "orig"
  $plutil -convert json -- "bare-wrapper"
  jq --argjson keys "$keys" "$jqfilter" <"orig" >"filtered"
  cat "bare-wrapper" "filtered" | jq -s add >"final"
  $plutil -convert xml1 -- "final"

  cp "final" "$to"
  popd >/dev/null
}

function sync_dock() {
  # Make sure all environment variables are cleared that might affect dockutil
  unset SUDO_USER

  # Array of applications to sync
  declare -a apps=("$@")

  # Iterate through each provided app
  for app_path in "${apps[@]}"; do
    if [ -d "$app_path" ]; then
      # Extract the name of the app from the path
      app_name=$(basename "$app_path")
      app_name=${app_name%.*} # Remove the '.app' extension
      resolved_path=$(realpath "$app_path")

      # Find the current Dock item for the app, if it exists
      current_dock_item=$(dockutil --list --no-restart | grep "$app_name.app" | awk -F "\t" '{print $1}' || echo "")

      if [ -n "$current_dock_item" ]; then
        # The app is currently in the Dock, attempt to replace it
        echo "Updating $app_name in Dock..."
        dockutil --add "$resolved_path" --replacing "$current_dock_item" --no-restart
      else
        # The app is not in the Dock; you might choose to add it or do nothing
        echo "$app_name is not currently in the Dock."
      fi
    else
      echo "Warning: Provided path $app_path is not valid."
    fi
  done

  # Restart the Dock to apply changes
  $killall Dock
}

function mktrampoline() {
  local app="$1"
  local trampoline="$2"

  if [[ ! -d $app ]]; then
    echo "app path is not directory."
    return 1
  fi

  cmd="do shell script \"open '$app'\""
  $osacompile -o "$trampoline" -e "$cmd"
  sync_icons "$app" "$trampoline"
  copy_paths "$(realpath "$app/Contents/Info.plist")" "$(realpath "$trampoline/Contents/Info.plist")" "${copyable_app_props[@]}"
}

function sync_trampolines() {
  [[ ! -d "$1" ]] && echo "Source directory does not exist" && return 1

  if [[ -d "$2" ]]; then
    rm -rf "$2"
  fi
  mkdir -p "$2"

  apps=("$1"/*.app)

  for app in "${apps[@]}"; do
    trampoline="$2/$(basename "$app")"
    mktrampoline "$app" "$trampoline"
  done
  sync_dock "${apps[@]}"
}