#!/bin/sh

# Halt execution immediately on failure.
# Note there are some scenarios in which this will not exit; see
# https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
# for additional details.
set -e

is_ci=
for i in "$@"; do
    case "$i" in
        -y)
        is_ci=true
        shift # Past argument=value
        ;;
        --install-prefix=*)
        installPrefix="${i#*=}"
        shift # past argument=value
        ;;
    esac
done

# If install-prefix is not passed, use default value
if [ -z "$installPrefix" ]; then
    installPrefix=/usr/local
fi

# Ensure install directory exists
if [ ! -d "$installPrefix" ]; then
    echo "The folder $installPrefix does not exist"
    exit
fi

# In non-ci scenarios, advertise what we will be doing and
# give user the option to exit.
if [ -z $is_ci ]; then
    echo "This script will download, compile, and install Git Credential Manager to:

    $installPrefix/bin

Git Credential Manager is licensed under the MIT License: https://aka.ms/gcm/license"

	while true; do
        # Display prompt once before reading input
        printf "Do you want to continue? [Y/n] "

        # Prefer reading from the controlling terminal (TTY) when available,
        # so that input works even if the script is piped (e.g. curl URL | sh)
        if [ -r /dev/tty ]; then
            read yn < /dev/tty
        # If no TTY is available, attempt to read from standard input (stdin)
        elif ! read yn; then
            # If input is not possible via TTY or stdin, assume a non-interactive environment
            # and abort with guidance for automated usage
            echo "Interactive prompt unavailable in this environment. Use 'sh -s -- -y' for automated install."
            exit 1
        fi

        case "$yn" in
            [Yy]*|"") break ;;
            [Nn]*) exit ;;
            *) echo "Please answer yes or no." ;;
        esac
    done
fi

install_packages() {
    pkg_manager=$1
    install_verb=$2
    packages=$3

    for package in $packages; do
        # Ensure we don't stomp on existing installations.
        if type $package >/dev/null 2>&1; then
            continue
        fi

        if [ $pkg_manager = apk ]; then
            $sudo_cmd $pkg_manager $install_verb $package
        elif [ $pkg_manager = zypper ]; then
            $sudo_cmd $pkg_manager -n $install_verb $package
        elif [ $pkg_manager = pacman ]; then
            $sudo_cmd $pkg_manager --noconfirm $install_verb $package
        else
            $sudo_cmd $pkg_manager $install_verb $package -y
        fi
    done
}

ensure_dotnet_installed() {
    if [ -z "$(verify_existing_dotnet_installation)" ]; then
        curl -LO https://dot.net/v1/dotnet-install.sh
        chmod +x ./dotnet-install.sh
        bash -c "./dotnet-install.sh --channel 8.0"

        # Since we have to run the dotnet install script with bash, dotnet isn't
        # added to the process PATH, so we manually add it here.
        cd ~
        export DOTNET_ROOT=$(pwd)/.dotnet
        add_to_PATH $DOTNET_ROOT
    fi
}

verify_existing_dotnet_installation() {
    # Get initial pieces of installed sdk version(s).
    sdks=$(dotnet --list-sdks | cut -c 1-3)

    # If we have a supported version installed, return.
    supported_dotnet_versions="8.0"
    for v in $supported_dotnet_versions; do
        if [ $(echo $sdks | grep "$v") ]; then
            echo $sdks
        fi
    done
}

add_to_PATH () {
  for directory; do
    if [ ! -d "$directory" ]; then
        continue; # Skip nonexistent directory.
    fi
    case ":$PATH:" in
        *":$directory:"*)
            break
        ;;
        *)
            export PATH=$PATH:$directory
        ;;
    esac
  done
}

apt_install() {
    pkg_name=$1

    $sudo_cmd apt update
    $sudo_cmd apt install $pkg_name -y 2>/dev/null
}

print_unsupported_distro() {
    prefix=$1
    distro=$2

    echo "$prefix: $distro is not officially supported by the GCM project."
    echo "See https://gh.io/gcm/linux for details."
}

version_at_least() {
	[ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]
}

sudo_cmd=

# If the user isn't root, we need to use `sudo` for certain commands
# (e.g. installing packages).
if [ -z "$sudo_cmd" ]; then
    if [ `id -u` != 0 ]; then
        sudo_cmd=sudo
    fi
fi

eval "$(sed -n 's/^ID=/distribution=/p' /etc/os-release)"
eval "$(sed -n 's/^VERSION_ID=/version=/p' /etc/os-release | tr -d '"')"
case "$distribution" in
    debian | ubuntu)
        $sudo_cmd apt update
        install_packages apt install "curl git"

        # Install dotnet packages and dependencies if needed.
        if [ -z "$(verify_existing_dotnet_installation)" ]; then
            # First try to use native feeds (Ubuntu 22.04 and later).
            if ! apt_install dotnet8; then
                # If the native feeds fail, we fall back to
                # packages.microsoft.com. We begin by adding the dotnet package
                # repository/signing key.
                $sudo_cmd apt update && $sudo_cmd apt install wget -y
                curl -LO https://packages.microsoft.com/config/"$distribution"/"$version"/packages-microsoft-prod.deb
                $sudo_cmd dpkg -i packages-microsoft-prod.deb
                rm packages-microsoft-prod.deb

                # Proactively install tzdata to prevent prompts.
                export DEBIAN_FRONTEND=noninteractive
                $sudo_cmd apt install -y --no-install-recommends tzdata

                $sudo_cmd apt update
                $sudo_cmd apt install apt-transport-https -y
                $sudo_cmd apt update
                $sudo_cmd apt install dotnet-sdk-8.0 dpkg-dev -y
            fi
        fi
    ;;
    fedora | centos | rhel | ol)
        $sudo_cmd dnf upgrade -y

        # Install dotnet/GCM dependencies.
        install_packages dnf install "curl git krb5-libs libicu openssl-libs zlib findutils which bash"

        ensure_dotnet_installed
    ;;
    alpine)
        $sudo_cmd apk update

        # Install dotnet/GCM dependencies.
        # Alpine 3.14 and earlier need libssl1.1, while later versions need libssl3.
        if ( version_at_least "3.15" $version ) then
            libssl_pkg="libssl3"
        else
            libssl_pkg="libssl1.1"
        fi

        install_packages apk add "curl git icu-libs krb5-libs libgcc libintl $libssl_pkg libstdc++ zlib which bash coreutils gcompat"

        ensure_dotnet_installed
    ;;
    sles | opensuse*)
        $sudo_cmd zypper -n update

        # Install dotnet/GCM dependencies.
        install_packages zypper install "curl git find krb5 libicu"

        ensure_dotnet_installed
    ;;
    arch)
        print_unsupported_distro "WARNING" "$distribution"

        # --noconfirm required when running from container
        $sudo_cmd pacman -Syu --noconfirm

        # Install dotnet/GCM dependencies.
        install_packages pacman -Sy "curl git glibc gcc krb5 icu openssl libc++ zlib"

        ensure_dotnet_installed
    ;;
    mariner)
        print_unsupported_distro "WARNING" "$distribution"
        $sudo_cmd tdnf update -y

        # Install dotnet/GCM dependencies.
        install_packages tdnf install "curl ca-certificates git krb5-libs libicu openssl-libs zlib findutils which bash awk"

        ensure_dotnet_installed
    ;;
    *)
        print_unsupported_distro "ERROR" "$distribution"
        exit
    ;;
esac

# Detect if the script is part of a full source checkout or standalone instead.
script_path="$(cd "$(dirname "$0")" && pwd)"
toplevel_path="${script_path%/src/linux/Packaging.Linux}"
if [ "z$script_path" = "z$toplevel_path" ] || [ ! -f "$toplevel_path/Git-Credential-Manager.sln" ]; then
    toplevel_path="$PWD/git-credential-manager"
    test -d "$toplevel_path" || git clone https://github.com/git-ecosystem/git-credential-manager
fi

if [ -z "$DOTNET_ROOT" ]; then
    DOTNET_ROOT="$(dirname $(which dotnet))"
fi

cd "$toplevel_path"
$sudo_cmd env "PATH=$PATH" $DOTNET_ROOT/dotnet build ./src/linux/Packaging.Linux/Packaging.Linux.csproj -c Release -p:InstallFromSource=true -p:installPrefix=$installPrefix
add_to_PATH "$installPrefix/bin"
