#!/usr/bin/env bash
# vim:et:ts=2:sts=2:sw=2

# Get absolute repository root (especially when symlinked)
ROOT="$(realpath "$(dirname \"$(dirname \"$(readlink -f \"${BASH_SOURCE[0]}\")\")\")")"

# Load accompanying bash library
source "$ROOT/lib/lib.bash"

BUNDLE_NAME="$1"
shift
INPUTS=("$@")

if [ -z "$BUNDLE_NAME" ] || [ ${#INPUTS[@]} -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
  say "
  USAGE:
    $(g "${BASH_SOURCE##*/}")  $(c bundle-name)  $(c pkg1)  [$(c /path/to/dir) ...]

  DESCRIPTION:
    Creates a package bundle archive containing specified packages and their
    dependencies. It accepts repository package names, paths to pre-built package
    files (.pkg.tar.zst), and directories containing such files.

    Arbitrary files passed as arguments will also be included in the bundle.

  EXAMPLE:
    # Create a bundle with a repo package, a local file, and everything in a dir
    sudo ${BASH_SOURCE##*/} my-tools-bundle supertuxkart ./xmoto-1.0.pkg.tar.zst /usr/local/src/
"
  exit 1
fi

# Exit now if not run as admin
assert-is-admin
say "This script will need root access via $(c sudo)"
sudo echo thank you

if [[ ! "$BUNDLE_NAME" =~ -bundle$ ]]; then
  warn "Bundle name $(c "$BUNDLE_NAME") does not end with '-bundle'."
  warn "It may not be installable with tl-comp-pull."
fi

DEST_DIR="/srv/packages"
run mkdir -p "$DEST_DIR"

if [ -f "$DEST_DIR/$BUNDLE_NAME.tar.gz" ]; then
  err "Bundle $(c "$DEST_DIR/$BUNDLE_NAME.tar.gz") already exists...exiting."
  exit 1
fi

REPO_PKGS=()
LOCAL_FILES=()
LOCAL_PKG_NAMES=()

say "Processing inputs..."
for input in "${INPUTS[@]}"; do
  if [ -d "$input" ]; then
    say "Scanning directory: $(c "$input")"
    while IFS= read -r -d '' f; do
      abspath=$(realpath "$f")
      LOCAL_FILES+=("$abspath")
    done < <(find "$input" -maxdepth 1 -name "*.pkg.tar.*" -print0)

  elif [ -f "$input" ]; then
    abspath=$(realpath "$input")
    LOCAL_FILES+=("$abspath")

  else
    # This is likely a repository package name
    REPO_PKGS+=("$input")
  fi
done

# Deduplicate local files early
if [ ${#LOCAL_FILES[@]} -gt 0 ]; then
  LOCAL_FILES=($(printf "%s\n" "${LOCAL_FILES[@]}" | sort -u))
fi

say "Analyzing local packages..."
for f in "${LOCAL_FILES[@]}"; do
  if [[ "$f" == *.pkg.tar.* ]] && pkgname=$(pacman -Qp "$f" 2>/dev/null | awk '{print $1}'); then
    say "  Found local package: $(c "$pkgname") ($(basename "$f"))"
    LOCAL_PKG_NAMES+=("$pkgname")
  else
    say "  Including plain file: $(c "$(basename "$f")")"
  fi
done

say "Resolving local dependencies..."
for f in "${LOCAL_FILES[@]}"; do
  if [[ "$f" == *.pkg.tar.* ]]; then
    # Get its dependencies
    DEPS=$(pacman -Qip "$f" 2>/dev/null | awk -F': ' '/^Depends On/ {print $2}')
    if [ -n "$DEPS" ]; then
      CLEAN_DEPS=$(echo "$DEPS" | sed -e 's/[<>=][^[:space:]]*//g' -e 's/ *([^)]*)//g')
      read -ra AD_DEPS <<<"$CLEAN_DEPS"
      for dep in "${AD_DEPS[@]}"; do
        [ "$dep" == "None" ] && continue
        # Only add if not already provided locally
        is_local=false
        for lp in "${LOCAL_PKG_NAMES[@]}"; do
          if [ "$dep" == "$lp" ]; then is_local=true; break; fi
        done

        if [ "$is_local" == false ]; then
          REPO_PKGS+=("$dep")
        fi
      done
    fi
  fi
done

TMP_DIR=$(mktemp -d)
run chmod 755 "$TMP_DIR"
cleanup() { run rm -rf "$TMP_DIR"; }

ALL_PKG_FILES=()

if [ ${#REPO_PKGS[@]} -gt 0 ]; then
  # Deduplicate repo pkgs and filter empty
  REPO_PKGS=($(printf "%s\n" "${REPO_PKGS[@]}" | sort -u))

  say "Resolving full dependency tree for repo packages..."
  # Use checked command substitution to avoid triggering aggressive traps prematurely
  if ! FULL_DEPS_RAW=$(pacman -Sp --print-format %n "${REPO_PKGS[@]}"); then
    err "Failed to resolve some repository packages: $(c "${REPO_PKGS[*]}")"
    exit 1
  fi
  readarray -t REPO_PKGS <<< "$FULL_DEPS_RAW"
  # Clean up empty elements/whitespace
  REPO_PKGS=($(printf "%s\n" "${REPO_PKGS[@]}"))

  say "Downloading repo packages and dependencies..."
  sudo pacman -Sw --cachedir "$TMP_DIR" --noconfirm --needed "${REPO_PKGS[@]}"

  if ! REPO_PKG_FILES_RAW=$(pacman -Swp --cachedir "$TMP_DIR" --noconfirm --needed "${REPO_PKGS[@]}"); then
    err "Failed to get file paths for bundled packages."
    exit 1
  fi
  mapfile -t REPO_PKG_FILES <<< "$REPO_PKG_FILES_RAW"
  # Strip file:// and remove empty elements
  REPO_PKG_FILES=($(printf "%s\n" "${REPO_PKG_FILES[@]}" | sed 's|^file://||'))

  ALL_PKG_FILES+=("${REPO_PKG_FILES[@]}")
fi

# Add local files to the list
ALL_PKG_FILES+=("${LOCAL_FILES[@]}")

if [ ${#ALL_PKG_FILES[@]} -eq 0 ]; then
  err "No packages found to bundle. Check package names and paths."
  exit 1
fi

# Deduplicate package files
readarray -t UNIQUE_PKG_FILES < <(printf "%s\n" "${ALL_PKG_FILES[@]}" | sort -u)

say "The following files will be bundled:"
for f in "${UNIQUE_PKG_FILES[@]}"; do
  say "  - $(basename "$f")"
done

say "Staging files in a temporary directory..."
for f in "${UNIQUE_PKG_FILES[@]}"; do
  # Copy if it's not already in TMP_DIR (from pacman -Sw)
  if [ "$(realpath "$(dirname "$f")")" != "$(realpath "$TMP_DIR")" ]; then
    run cp -v "$f" "$TMP_DIR/"
  fi
done

ARCHIVE_PATH="$DEST_DIR/$BUNDLE_NAME.tar.gz"
say "Creating bundle archive at $(c "$ARCHIVE_PATH")"
run tar -czf "$ARCHIVE_PATH" -C "$TMP_DIR" .

say "$(g "Success!") Bundle created."
say "Distribute $(c "$ARCHIVE_PATH") on your upstream server."
