#!/usr/bin/env sh

# set -x

# Allows to install software

# Find list of available installers in order of preference
# Read wanted packages and their availability
# Install packages per installer (after prompting)

# COMMON FUNCTIONS

# GLOBAL VARIABLES

# TODO Lock or temp folder
CACHE_DIR="${XDG_CACHE_DIR:-$HOME/.cache}/installSoftware"
INSTALLERS_DIR="${CACHE_DIR}/installers"
INSTALLERS_LIST="${CACHE_DIR}/installers.list"

# TODO Package groups

# INSTALLER SPECIFIC FUNCTIONS

# Test if available
available_pkgbuild() {
    true
}

available_Tapt() {
    [ -f /data/data/com.termux/files/usr/bin/apt ]
}

# Update the database
update_Rpacman() {
    sudo pacman -Syu --noconfirm
}

update_Ryay() {
    yay -Syu --noconfirm --devel
}

update_Rapt() {
    # TODO non-interactive
    sudo apt update -y
    sudo apt upgrade -y
}

# Test if installed

installed_Rpacman() { # packageName
    pacman -Qq "$1" &> /dev/null
}

installed_Ryay() { # package
    installed_Rpacman "$@"
}

installed_pip() { # package
    pip show "$@" > /dev/null
}

installed_makepkg() { # package
    installed_Rpacman "$@"
}

installed_pkgbuild() { # package
    installed_Rpacman "$@"
    # TODO Might need all the systems package too
}

installed_apt() { # package
    dpkg -s "$1" &> /dev/null
}

# Test if available in the repositories

installable_aur() {
    curl --fail --silent --head "https://aur.archlinux.org/packages/$@" > /dev/null
}

installable_Rpacman() {
    pacman -Si "$1" &> /dev/null
}

installable_Ryay() {
    # yay -Ss "$1" > /dev/null
    installable_aur
}

installable_pkgbuild() {
    installable_aur
}

installable_makepkg() {
    installable_aur
}

installable_apt() {
    apt-cache show "$1" &> /dev/null
}

# Tell if it hadles mutiple argument for install

multiinstall_pacman() {
    true
}

multiinstall_pip() {
    true
}

multiinstall_apt() {
    true
}

# Really install

install_Rpacman() {
    sudo pacman -S --needed --noconfirm $@
}

install_Ryay() {
    yay -S --needed --noconfirm $@
}

install_Tapt() {
    apt install -y $@
}

install_Rapt() {
    sudo apt install -y $@
}

install_Rpip() {
    sudo pip install $@
}

install_Upip() {
    pip install --user $@
}

installable_Rmakepkg() {
    old_pwd="$PWD"
    TMP_DIR="$(mktemp -d /tmp/pkgbuild.XXXXXXXXXX)"
    cd "$TMP_DIR"
    wget "https://aur.archlinux.org/cgit/aur.git/snapshot/$1.tar.gz"
    tar xzvf "$1.tar.gz"
    cd "$1"
    makepkg -si
    cd "$old_pwd"
    rm -rf "$TMP_DIR"
}

# GENERIC INSTALLER FUNCTIONS

installerCanonicalName() { # installerName
    echo "${1:1}"
}

notImplementedFallback() { # command installerName
    echo "[NIMPL] Not implemented: command $1_$2"
    false
}

# Wrapper to run a command for an installer
commandInstaller() { # command installerName fallback args...
    # echo "[DEBUG] commandInstaller $*"
    command="$1"
    installerName="$2"
    fallback="$3"
    shift; shift; shift
    installerCName="$(installerCanonicalName "$installerName")"
    if command -v "${command}_$installerName" > /dev/null
    then
        "${command}_$installerName" "$@"
    elif command -v "${command}_$installerCName" > /dev/null
    then
        "${command}_$installerCName" "$@"
    else
        $fallback "$command" "$installerName" "$@"
    fi
}

# INSTALLER LIST FUNCTIONS

availableInstallerFallback() { # command installerName
    command -v "$(installerCanonicalName "$2")" > /dev/null
}

availableInstaller() { # installerName
    commandInstaller "available" "$1" availableInstallerFallback
}

# Clears the list of installers to use
resetInstallers() {
    rm -f "$INSTALLERS_LIST"
    touch "$INSTALLERS_LIST"
    rm -rf "$INSTALLERS_DIR"
    mkdir -p "$INSTALLERS_DIR"
}

addInstaller() { # installername
    if availableInstaller "$1"
    then
        echo "$1" >> "$INSTALLERS_LIST"
        rm -f "$INSTALLERS_DIR/$1"
        touch "$INSTALLERS_DIR/$1"
        return 0
    fi
    return 1
}


listInstallers() {
    cat "$INSTALLERS_LIST"
}

hasInstaller() { # installerName
    [ -f "${INSTALLERS_DIR}/$1" ]
}


# Installers that install in the user directory
addUserInstallers() {
    addInstaller Tapt # Termux
    # addInstaller Umakepkg # TODO broken
    addInstaller Upip
    # addInstaller Upkgbuild
}

# Installers that install with the system package manager
addSystemInstallers() {
    addInstaller Rapt
    addInstaller Rpacman
    addInstaller Ryay || addInstaller Rmakepkg
}

# Installers that install in the system root but not tracked with system package manager
addRootInstallers() {
    addInstaller Rnode
    addInstaller Rpip
    addInstaller Rpkgbuild

}

# UPDATING INSTALLER DATABASES

updateInstallers() {
    for installerName in $(listInstallers)
    do
        commandInstaller "update" "$installerName" true
    done
}

# MARKING PACKAGES TO INSTALL

markPackage() { # installer package
    # echo "[DEBUG] Marked $*"
    echo "$2" >> "$INSTALLERS_DIR/$1"
}

# Test if a package is installed
installedPackage() { # package
    for installerName in $(listInstallers)
    do
        if commandInstaller "installed" "$installerName" notImplementedFallback "$1"
        then
            return 0
        fi
    done
    return 1
}

installablePackageBy() { # installer package
    commandInstaller "installable" "$1" notImplementedFallback "$2"
}

# List packages to install by given installer
listPackagesForInstaller() { # installer
    cat "$INSTALLERS_DIR/$1"
}

isToInstall() { # package
    for installerName in $(listInstallers)
    do
        listPackagesForInstaller "$installerName" | grep -q "^$1\$" && return 0
    done
    return 1
}

# Mark package to installer list where available
tryPackage() { # packageNames
    # If package is already installed or marked to install, skip
    for package in "$@"
    do
        installedPackage "$package" && return 0
        isToInstall "$package" && return 0

    done
    # For each installer
    for installerName in $(listInstallers)
    do
        # For each name given
        for package in "$@"
        do
            # Mark if name available to installer
            if installablePackageBy "$installerName" "$package"
            then
                markPackage "$installerName" "$package"
                return 0
            fi
        done
    done
    echo "[ERR] Cannot install package with aliases: $*"
    return 1
}

i() {
    tryPackage "$@"
}

installPackages() {
    for installerName in $(listInstallers)
    do
        # If we can install multiple packages in one go
        if commandInstaller "multiinstall" "$installerName" false
        then
            packages=$(listPackagesForInstaller "$installerName")
            [ -z "$packages" ] && continue
            commandInstaller "install" "$installerName" notImplementedFallback $packages
        else
            for packageName in $(listPackagesForInstaller "$installerName")
            do
                commandInstaller "install" "$installerName" notImplementedFallback "$packageName"
            done
        fi
    done
}