#!/usr/bin/env bash

# Check that Python 3 is installed (by checking for python and python3 commands).
# If so, verify that python3 command is present.
# If Python 3 is not found on the system, try to install it and repeat the abose checks.

PROGNAME="${BASH_SOURCE[0]}"
HERE="$(cd "$(dirname "$PROGNAME")" &>/dev/null && pwd)"
ROOT=$(cd $HERE/../.. && pwd)
READIES=$ROOT/readies
. $READIES/shibumi/defs

export HOMEBREW_NO_AUTO_UPDATE=1

MODERN_PYTHON_VER=3.9.16

#----------------------------------------------------------------------------------------------

if [[ $1 == --help || $1 == help || $HELP == 1 ]]; then
	cat <<-END
		[ARGVARS...] getpy3 [--help|help]

		Argument variables:
		MODERN=0|1   Whether to install a modern Python (>3.6)
		MYPY=path    Use specific Python interpreter (install pip & requirements)
		CHECK=1      Only check and report, do not install
		PIP=0        Avoid installing pip
		PIP=version  Also install pip of the specified version
		VENV=1       Install virtualenv
		VENV=dir     Install virtualenv, create one in `dir`
		FORCE=1      Install even if present
		FIX=1        Create /usr/local/bin/python3 symlink (default on macOS)
		VERBOSE=n    Verbosity level (1: print commands, 2: print results)
		NOP=1        Print commands, don't execute
		HELP=1       Print help

	END
	exit 0
fi

note() {
	if (( VERBOSE >= 1 )); then
		echo "# $@"
	fi
}

#----------------------------------------------------------------------------------------------

OP=""
[[ $NOP == 1 ]] && OP=echo

USER_MYPY="$MYPY"

if [[ -f /etc/os-release ]]; then
	osver=$(source /etc/os-release; echo "${ID}${VERSION_ID}")
fi
arch=$(uname -m)

# [[ $VERBOSE == 1 ]] && { set -x; PS4='$LINENO: '; }
V="$VERBOSE"
[[ -z $PIP ]] && PIP=1

SUDO=
if [[ $(id -u) != 0 ]]; then
	if is_command sudo; then
		SUDO=sudo
	fi
fi

# (This is suspended and remains here for the next time pip is broken.)
# pip 20.x is broken is the sense that it will not respect virtualenv's system-site-packages
# thus requiring packages to be built from source in order to be installed.
# Until this is fixed we stick to pip 19.3.1.
# [[ $PIP == 1 ]] && PIP=19.3.1

#----------------------------------------------------------------------------------------------

# the end state should be:
# in a python2 venv: `python` command (don't fix)
# in a non-python2 venv or outside venv:
#   mandatory: `python3` command (fix)
#   optional: `python` command (don't fix)

fix_variants() {
	if [[ -n $VIRTUAL_ENV ]]; then
		[[ -n $PYTHON && "$PYTHON_VER" == "3"  ]] && return
	fi

	if [[ -z $PYTHON3 ]]; then
		if [[ -n $PYTHON && "$PYTHON_VER" == "3"  ]]; then
			note "create /usr/local/bin/python3 -> $PYTHON"
			runn ln -sf $PYTHON /usr/local/bin/python3
		fi
	fi
}

check_variants() {
	if [[ -n $USER_MYPY ]]; then
		note "using Python $USER_MYPY"
		PYTHON="$USER_MYPY"
		PYTHON3="$USER_MYPY"
		return
	fi
	PYTHON=$(command -v python)
	PYTHON3=$(command -v python3)

	MYPY=""
	PYTHON_VER=""

	if [[ -n $PYTHON ]]; then
		PYTHON_VER="$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1)"
		[[ $PYTHON_VER == 3 ]] && MYPY=$PYTHON
	fi

	if [[ -n $PYTHON3 ]]; then
		MYPY=$PYTHON3
		note "using $MYPY"
	fi

	MINOR="$($MYPY --version 2>&1 | awk '{print $2}' | cut -d. -f2)"
	note "minor version is $MINOR"
	
	# Python3 <= 3.4 breaks contemporary pip
	if (( MINOR <= 5 )); then
		note "minor version < 6: forcing installation"
		FORCE=1
	fi
	if (( MINOR <= 6 )); then
		note "minor version <= 6: forcing modern version installation"
		FORCE_MODERN=1
	fi

	if [[ -n $FIX ]]; then fix_variants; fi
}

#----------------------------------------------------------------------------------------------

install_python_on_old_ubuntu() {
	# Possible workaround - currently disabled
	# PYTHON=1 $READIES/bin/getconda

	gpg_bypass="--allow-unauthenticated"
	
	# ppa:deadsnakes no longer provides for xenial, trusty
	runn $SUDO apt-get install --fix-missing -y software-properties-common
	runn $SUDO add-apt-repository -y ppa:fkrull/deadsnakes
	runn $SUDO apt-get -qq $gpg_bypass update
	runn $SUDO apt-get install $gpg_bypass --fix-missing -y python3.6
	runn ln -sf `command -v python3.6` /usr/local/bin/python3
	runn $SUDO apt-get install $gpg_bypass --fix-missing -y python3.6-venv
}

install_python36_on_debian9_x64() {
	runn $SUDO $READIES/bin/getget

	runn $SUDO apt-get install --fix-missing -y \
		software-properties-common apt-transport-https \
		unzip lsb-release gnupg2 \
		libbz2-dev libssl-dev libsqlite3-dev libreadline-dev libgdbm-dev \
		tk-dev libncursesw5-dev liblzma-dev uuid-dev libexpat1-dev
	tempd=$(mktemp -d /tmp/py3.XXXXXX)
	cd $tempd
	runn wget https://github.com/chriskuehl/python3.6-debian-stretch/releases/download/v3.6.3-1-deb9u1/{python3.6_3.6.3-1.deb9u1_amd64,python3.6-minimal_3.6.3-1.deb9u1_amd64,python3.6-dev_3.6.3-1.deb9u1_amd64,libpython3.6_3.6.3-1.deb9u1_amd64,libpython3.6-minimal_3.6.3-1.deb9u1_amd64,libpython3.6-stdlib_3.6.3-1.deb9u1_amd64,libpython3.6-dev_3.6.3-1.deb9u1_amd64}.deb
	runn $SUDO dpkg -i *.deb
	if [[ -f /usr/bin/python3.6 ]]; then
		runn ln -sf /usr/bin/python3.6 /usr/local/bin/python3
		MYPY=/usr/local/bin/python3
	fi
	cd $HERE
	rm -rf $tempd
}

install_python() {
	note "installing Python"

	if [[ $(uname) == Darwin ]]; then
		if ! is_command brew; then
			eprint "Unable to install Python without brew. Aborting."
			eprint "Please install brew and retry."
			exit 1
		fi

		# try to fix /usr/local/bin/python on macOS unless stated otherwize
		[[ $FIX != 0 ]] && FIX=1
	fi

	if is_command apt-get; then
		export DEBIAN_FRONTEND=noninteractive
		runn $SUDO apt-get -qq update --fix-missing
		if [[ $FORCE_MODERN == 1 && $MODERN != 0 ]]; then
			note "install version $MODERN_PYTHON_VER using pyenv"
			runn PYTHON=$MODERN_PYTHON_VER GLOBAL=1 $READIES/bin/getpyenv
			$OP . $(get_profile_d)/pyenv.sh
		elif [[ $osver == ubuntu14.04 || $osver == ubuntu16.04 ]]; then
			install_python_on_old_ubuntu
		elif [[ $osver == debian9 && $arch == x86_64 ]]; then
			install_python36_on_debian9_x64
		else
			runn $SUDO apt-get install --fix-missing -y python3
		fi
	elif is_command dnf; then
		if [[ $FORCE_MODERN == 1 && $MODERN != 0 ]]; then
			note "install version $$MODERN_PYTHON_VER using pyenv"
			runn PYTHON=$MODERN_PYTHON_VER GLOBAL=1 $READIES/bin/getpyenv
			$OP . $(get_profile_d)/pyenv.sh
		else
			runn $SUDO dnf install -y python3
		fi
	elif is_command yum; then
		runn $SUDO yum install -y epel-release
		if [[ $FORCE_MODERN == 1 && $MODERN != 0 ]]; then
			note "install version $$MODERN_PYTHON_VER using pyenv"
			runn PYTHON=$MODERN_PYTHON_VER GLOBAL=1 $READIES/bin/getpyenv
			$OP . $(get_profile_d)/pyenv.sh
		else
			runn $SUDO yum install -y python36
		fi
	elif is_command zypper; then
		runn $SUDO zypper install -y python3
	elif is_command apk; then
		runn $SUDO apk update
		runn $SUDO apk add python3 python3-dev
	elif is_command brew; then
		runn brew install python3
	elif is_command pkg; then
		runn $SUDO pkg install -y python3
	elif is_command pacman; then
		runn $SUDO pacman -Syy --noconfirm python
	fi
}

#----------------------------------------------------------------------------------------------

install_pip() {
	note "installing pip"

	if [[ $FORCE != 1 ]]; then
		[[ $($MYPY -m pip --version > /dev/null 2>&1; echo $?) == 0 ]] && return
	fi
	pipspec=""
	[[ $PIP != 1 ]] && pipspec="pip==$PIP"

	local packs="ca-certificates wget"

	if is_command apt-get; then
		export DEBIAN_FRONTEND=noninteractive
		runn $SUDO apt-get -qq update --fix-missing
		runn $SUDO apt-get install --fix-missing -y $packs
		runn @ <<-EOF
			$SUDO apt-get install --fix-missing -y python3-distutils || $SUDO apt-get install --fix-missing -y python3-distutils-extra
			EOF
	elif is_command dnf; then
		runn $SUDO dnf install -y $packs
	elif is_command yum; then
		runn $SUDO yum install -y $packs
	elif is_command zypper; then
		packs+=" python-xml"
		runn $SUDO zypper install -y $packs
	elif is_command apk; then
		runn $SUDO apk update
		runn $SUDO apk add $packs
	elif is_command brew; then
		runn brew install wget
	elif is_command pkg; then
		runn $SUDO pkg install -y $packs
	elif is_command pacman; then
		runn $SUDO pacman -Syy --noconfirm $packs
	fi

	$MYPY -c 'import sys; print("{major}.{minor}".format(major=sys.version_info.major, minor=sys.version_info.minor))' | grep '3.6' &>/dev/null
	if [[ $? == 0 ]]; then
		pypi_url='https://bootstrap.pypa.io/pip/3.6/get-pip.py'
	else
		pypi_url='https://bootstrap.pypa.io/get-pip.py'
	fi
	runn wget -O /tmp/get-pip.py $pypi_url
	# currently can fail on debian-compat platforms due to distutils:
	# `python3 -m pip --version` will fail to find pip
	runn "$MYPY /tmp/get-pip.py $pipspec || true"
	runn rm -f /tmp/get-pip.py
	runn $MYPY -m pip install $PIP_USER --upgrade pip
}

#----------------------------------------------------------------------------------------------

check_variants

# [[ -n $MYPY && $PIP == 0 && $FORCE != 1 ]] && exit 0

if [[ $CHECK == 1 ]]; then
	# Make sure pip is installed
	if $MYPY -m pip --version &> /dev/null; then exit 0; fi
	exit 1
fi

[[ -z $MYPY || $FORCE == 1 || $FORCE_MODERN == 1 ]] && install_python

if [[ $(uname) == Darwin ]]; then
	if is_command brew; then
		# The following is for avoiding python to be upgraded by brew silently,
		# hence disrupting pip installaitons.
		# This however gets trickier, as brew might not recognize a currently installed
		# python version, and upgrade will fail. Thus, we need to force installation, and
		# still be graceful about failures.
		runn "brew upgrade python || brew install python3 || true"

		# macOS can have system-installed /usr/bin/python3 as well as several /usr/local/bin/python3.x versions.
		# The latter is typically installed by brew, which may or may not arrange a /usr/local/bin/python3.
		# Thus, if /usr/local/bin/python3 exists, we let it be. Otherwise, we opt for the latest.
		if [[ ! -e /usr/local/bin/python3 ]]; then
			LATEST_MINOR="$(brew list --formula | grep python | cut -d@ -f2 | cut -d. -f2 | sort -nr | head -1)"
			if [[ -f /usr/local/bin/python3.$LATEST_MINOR ]]; then
				(cd /usr/local/bin; runn ln -s "python3.$LATEST_MINOR" python3;)
				hash -r
			fi
		fi
	fi
fi

check_variants
if ! is_command $MYPY; then
	eprint "Cannot install Python3. Aborting."
	exit 1
fi

if [[ -z $VIRTUAL_ENV ]]; then
	PIP_USER="--user"
else
	PIP_USER=""
fi

[[ $PIP != 0 ]] && install_pip

REQ=$READIES/paella/requirements.txt
if [[ -n $VENV && $VENV != 0 ]]; then
	runn $MYPY -m pip install ${PIP_USER} virtualenv
	[[ $VENV == 1 ]] && VENV=$HOME/venv
	runn $MYPY -m virtualenv $VENV --system-site-packages
	runn "{ . $VENV/bin/activate && python -m pip install -r $REQ; }"
else
	runn $MYPY -m pip install ${PIP_USER} -r $REQ
fi
if (( VERBOSE >= 1 )); then
	runn $MYPY -m pip list -v
fi

exit 0
