#!/bin/bash
# Author: crims0n <https://minios.dev>

VERSION="1.2.1"
BEXT="sb"
COMP_TYPE="zstd"
export TEXTDOMAIN="minios-tools"

help() {
    echo "$(gettext "Usage"): $(basename $0) COMMAND [OPTIONS] PACKAGE1 [PACKAGE2] [...]"
    echo "$(gettext "Installs packages from repositories and packages them into a module.")"
    echo ""
    echo "$(gettext "Commands"):"
    echo "  install                     $(gettext "Install package(s)")"
    echo "  upgrade                     $(gettext "Upgrade installed packages")"
    echo ""
    echo "$(gettext "Options"):"
    echo "  -n, --name  NAME            $(gettext "Use NAME as the filename for the module instead of PACKAGE1").$BEXT"
    echo "  -l, --level LEVEL           $(gettext "Use LEVEL as the filter level")"
    echo "  -c, --comp TYPE             $(gettext "Compression type (zstd, gzip, lzo, xz). Default: zstd")"
    echo "  -b, --bext EXT              $(gettext "Bundle extension. Default: sb")"
    echo "      --help                  $(gettext "Display this help and exit")"
    echo "      --version               $(gettext "Display version information and exit")"
    echo ""
    echo "$(gettext "Apt Options (for install/upgrade command)"):"
    echo "  -y, --yes                   $(gettext "Automatic yes to prompts")"
    echo "      --allow-downgrades      $(gettext "Allow downgrades of packages")"
    echo "      --install-recommends    $(gettext "Consider recommended packages as a dependency for installing")"
    echo "      --install-suggests      $(gettext "Consider suggested packages as a dependency for installing")"
    echo "      --no-install-recommends $(gettext "Do not consider recommended packages as a dependency for installing")"
    echo "      --no-install-suggests   $(gettext "Do not consider suggested packages as a dependency for installing")"
    echo "  -t, --target-release        $(gettext "Default release to install packages from")"
    echo ""
    echo "$(gettext "Examples"):"
    echo "   $(basename $0) install chromium chromium-sandbox"
    echo "   $(basename $0) install -y --level 03 chromium chromium-sandbox"
    echo "   $(basename $0) install -y --no-install-recommends ./google-chrome-stable_current_amd64.deb -n 06-google-chrome.$BEXT -l 3"
    echo "   $(basename $0) upgrade"
    exit 0
}

brief_help() {
    echo "$(gettext "Usage"): $(basename $0) COMMAND [OPTIONS] PACKAGE1 [PACKAGE2] [...]"
    echo "$(gettext "Try") '$(basename $0) --help' $(gettext "for more information.")"
    exit 1
}

version() {
    echo "$(basename $0) $VERSION"
    exit 0
}

aufs_is_supported() {
    grep -q aufs /proc/filesystems
}

if [ $# -eq 0 ]; then
    brief_help
fi

APT_COMMAND=$1
shift

APT_COMMANDS="install upgrade"

if [ "$APT_COMMAND" == "--help" -o "$APT_COMMAND" == "--version" ]; then
    case "$APT_COMMAND" in
    --help)
        help
        ;;
    --version)
        version
        ;;
    esac
else
    if [[ ! $APT_COMMANDS =~ (^|[[:space:]])$APT_COMMAND($|[[:space:]]) ]]; then
        echo "Invalid command entered. Please enter a valid command."
        echo "Valid commands are: $APT_COMMANDS"
        echo ""
        brief_help
    fi
fi

VALID_APT_OPTIONS=(-y --yes --allow-downgrades --install-recommends --install-suggests --no-install-recommends --no-install-suggests --target-release)

while [ $# -gt 0 ]; do
    case "$1" in
    -n | --name)
        FILENAME="$2"
        shift 2
        ;;
    -l | --level)
        FILTER_LEVEL=$(printf "%02d" "$2")
        shift 2
        ;;
    -c | --comp)
        COMP_TYPE="$2"
        shift 2
        ;;
    -b | --bext)
        BEXT="$2"
        shift 2
        ;;
    --help)
        help
        ;;
    --version)
        version
        ;;
    *)
        if [[ "$1" == -* ]]; then
            if [[ ! " ${VALID_APT_OPTIONS[@]} " =~ " $1 " ]]; then
                echo "'$1' is not a valid apt option."
                brief_help
            fi

            APT_OPTIONS+=" $1"
        else
            PACKAGES+=" $1"
        fi
        shift
        ;;
    esac
done

if [ "$(id -u)" != "0" ]; then
    echo "This script must be run as root."
    brief_help
fi

if [ ! "$PACKAGES" -a "$APT_COMMAND" == "install" ]; then
    brief_help
fi

if [ -d "/run/initramfs/memory/bundles" ]; then
    BUNDLES="/run/initramfs/memory/bundles"
    SYSTEMCHANGES="/run/initramfs/memory/changes"
elif [ -d "/memory/bundles" ]; then
    BUNDLES="/memory/bundles"
    SYSTEMCHANGES="/memory/changes"
fi

# Overlayfs requires one more subdir. Detect it this way
if [ -d "$SYSTEMCHANGES/changes" ] && [ -d "$SYSTEMCHANGES/workdir" ] && [ "$(ls -1 "$SYSTEMCHANGES" | egrep -v '^changes$' | egrep -v '^workdir')" = "" ]; then
   SYSTEMCHANGES="$SYSTEMCHANGES/changes"
fi

TMP=$(mktemp -d "$SYSTEMCHANGES"/build2sb.XXXXX)
CHANGES="$TMP/changes"
UNION="$TMP/union"

trap 'if [ "$RESOLVCONFLNK" = "true" ]; then rm -f "$CHANGES/etc/resolv.conf" 2>/dev/null; else umount "$UNION/etc/resolv.conf" 2>/dev/null; fi; umount "$UNION/sys" 2>/dev/null; umount "$UNION/proc" 2>/dev/null; umount "$UNION/dev/pts" 2>/dev/null; umount "$UNION/dev" 2>/dev/null; rm "$CHANGES/install" 2>/dev/null; rmdir "$CHANGES/*" 2>/dev/null; umount "$UNION"; umount "$TMP" 2>/dev/null; rm -rf "$TMP"' EXIT

mkdir -p "$CHANGES"
mkdir -p "$UNION"

ROFLAG=""
if aufs_is_supported; then
    ROFLAG="=ro"
fi

if [ -n "$FILTER_LEVEL" ]; then
    for BUNDLE in $(ls -1dr "$BUNDLES"/*); do
        if [[ $(basename "$BUNDLE") =~ ^[0-9]+ ]]; then
            if [ ${BASH_REMATCH[0]} -gt $FILTER_LEVEL ]; then
                continue
            fi
        fi
        LOWERS+=$(printf '%s%s:' "$BUNDLE" "$ROFLAG")
    done
else
    for BUNDLE in $(ls -1dr "$BUNDLES"/*); do
        LOWERS+=$(printf '%s%s:' "$BUNDLE" "$ROFLAG")
    done
fi

LOWERS=${LOWERS/%:/}

if aufs_is_supported; then
    mount -t aufs -o br="$CHANGES"=rw:"$LOWERS" none "$UNION"
else
    mkdir $TMP/work
    mount -t overlay -o lowerdir="$LOWERS",upperdir="$CHANGES",workdir="$TMP"/work overlay "$UNION"
fi

for DIR in boot dev proc sys tmp media mnt run; do
    mkdir -p "$UNION/$DIR"
done

chmod ugo+rwx "$UNION/tmp"

for DIR in dev dev/pts proc sys; do
    mount --bind "/$DIR" "$UNION/$DIR"
done

for PACKAGE in $PACKAGES; do
    if [[ $PACKAGE =~ \/ ]]; then
        PACKAGE_PATH=$(realpath $PACKAGE)
        if [ -f $PACKAGE_PATH ]; then
            cp $PACKAGE_PATH "$UNION/"
            PACKAGE_NAME="/"$(basename $PACKAGE_PATH)

            PACKAGES=${PACKAGES//$PACKAGE/$PACKAGE_NAME}
        else
            echo "File $PACKAGE_PATH does not exist."
            continue
        fi
    fi
done

if [ "$APT_COMMAND" == "install" ]; then
    echo "Executing: apt-get update; apt-get install $APT_OPTIONS $PACKAGES"
    echo "export LANG=C; apt-get update; apt-get $APT_COMMAND $APT_OPTIONS $PACKAGES" >"$UNION/install"
elif [ "$APT_COMMAND" == "upgrade" ]; then
    echo "Executing: apt-get update; apt-get upgrade $APT_OPTIONS"
    echo "export LANG=C; apt-get update; apt-get $APT_COMMAND $APT_OPTIONS" >"$UNION/install"
fi

chmod ugo+x "$UNION/install"
if [ -L "$UNION/etc/resolv.conf" ]; then
    rm "$UNION/etc/resolv.conf"
    echo "nameserver 8.8.8.8" >"$UNION/etc/resolv.conf"
    RESOLVCONFLNK="true"
else
    mount --bind "/etc/resolv.conf" "$UNION/etc/resolv.conf"
fi
if ! /usr/sbin/chroot "$UNION" /install; then
    FAILED="true"
else
    FAILED="false"
fi

for PACKAGE in $PACKAGES; do
    if [[ $PACKAGE =~ \/ ]]; then
        rm "$UNION$PACKAGE_NAME"
    fi
done

if [ "$RESOLVCONFLNK" = "true" ]; then
    rm -f "$CHANGES/etc/resolv.conf"
else
    umount "$UNION/etc/resolv.conf"
fi
umount "$UNION/sys"
umount "$UNION/proc"
umount "$UNION/dev/pts"
umount "$UNION/dev"
rm "$CHANGES/install"
rmdir "$CHANGES/*" 2>/dev/null

if [ "$FAILED" = "false" ]; then
    if [ ! "$FILENAME" ]; then
        FILENAME="$(echo "$PACKAGES" | awk '{print $1}' | sed 's/\///g').$BEXT"
        if [ -n "$FILTER_LEVEL" ]; then
            PREFIX="$(printf "%02d" $((10#$FILTER_LEVEL + 1)))-"
        else
            PREFIX=""
        fi
        FILENAME="$PREFIX$FILENAME"
    fi

    echo "Saving changes made by installing packages into a module $FILENAME..."
    savechanges "$FILENAME" "$CHANGES" -c "$COMP_TYPE"
fi
