#!/usr/bin/env bash

set -euo pipefail

libthemis_install_raise() {
    echo -e "\\nERROR: $*\\n" >&2
    exit 1
}

libthemis_install_detect_os() {
    os_type=$(uname | tr '[:upper:]' '[:lower:]')
    case "$os_type" in
        (linux)
            if [[ -f "/etc/os-release" ]]; then
                . /etc/os-release
                os="${ID,,}"
                os_ver="${VERSION_ID%%.*}"
                case "$os" in
                    (debian)
                        os_ver_name="${VERSION#*(}"
                        os_ver_name="${os_ver_name%)}"
                        ;;
                    (*)
                        os_ver_name="${VERSION_CODENAME:-${os_ver}}"
                        ;;
                esac
            else
                libthemis_install_raise "can not detect Linux version."
            fi
            ;;
        (*)
            libthemis_install_raise "OS family '$(uname)' is not supported."
            ;;
    esac
}

libthemis_install_os_consts() {
    if [[ "$os" == 'centos' ]]; then
        cmd_pkg='dnf'
        if [[ "$os_ver_name" == '7' ]]; then
            cmd_pkg='yum'
        fi
    fi
}

libthemis_install_help() {
echo "
Usage:
    libthemis_install.sh [option] ...

Options:
    common options:
        --method <method_name>
            Preferred installation method.
            package
                Install pre-built package. This is default behavior.
            source
                Build and install from sources.
        --branch <branch_name>
            Repository or github branch to install from.
            Default is 'master' for github and 'stable' for repository.
        --with-php
            Install PHP module.
        --with-jni
            Install Java module.
        --yes
            Do not request confirmations.
        --skip-install-required
            Do not install required packages. Suppose they already
            present in system.
        --help
            Show this help message.
    'package' method specific options:
        --without-dev
            Do not install *-dev packages.
    'source' method specific options:
        --without-packing
            By default this script will fetch and compile from sources, build
            deb or rpm package and install it. Add this option if you want
            to switch to traditional 'make install' procedure instead.
        --without-clean
            Do not remove source directory after install.

Examples:
    libthemis_install.sh
        Download and install pre-compiled package in your system.
    libthemis_install.sh --method source
        Download sources, build, pack into deb/rpm and install it.
    libthemis_install.sh --method source --without-packing
        Download sources, build and install it.
"
}

libthemis_install_parse_args() {
    flag_with_php=""
    flag_with_jni=""
    flag_yes=""
    flag_skip_install_required=""
    flag_wo_dev=""
    flag_wo_packing=""
    flag_wo_clean=""
    while (( $# > 0 )); do
        arg="$1"
        shift
        case "$arg" in
            (--method)
                [[ -n "${1:-}" ]] || libthemis_install_raise \
                    "method name must be present next to --method option."
                [[ "$1" =~ ^(package|source)$ ]] || \
                    libthemis_install_raise "unknown method '$1'."
                method="$1"
                shift
                ;;
            (--with-php)
                flag_with_php="1"
                ;;
            (--with-jni)
                flag_with_jni="1"
                ;;
            (--yes)
                flag_yes=" -y"
                ;;
            (--skip-install-required)
                flag_skip_install_required="1"
                ;;
            (--without-dev)
                flag_wo_dev="1"
                ;;
            (--without-packing)
                flag_wo_packing="1"
                ;;
            (--without-clean)
                flag_wo_clean="1"
                ;;
            (--branch)
                [[ -n "${1:-}" ]] || libthemis_install_raise \
                    "branch name must be present next to --branch option."
                branch="$1"
                shift
                ;;
            (help|--help|-?|--?)
                libthemis_install_help
                exit 0
                ;;
            (*)
                libthemis_install_help
                exit 1
                ;;
        esac
    done
    method="${method:-package}"
    if [[ "$method" == 'package' ]]; then
        branch="${branch:-stable}"
    else
        branch="${branch:-master}"
    fi
}

libthemis_install_check() {
    OS_SUPPORTED=(
        debian:bookworm
        debian:bullseye
        debian:buster
        debian:stretch
        ubuntu:noble
        ubuntu:jammy
        ubuntu:focal
        ubuntu:bionic
        ubuntu:xenial
        centos:8
    )
    [[ " ${OS_SUPPORTED[@]} " =~ " $os:$os_ver_name " ]] ||
    libthemis_install_raise "OS version '$os $os_ver_name' is not supported."
}

libthemis_install_cmd() {
    echo -e "\\n== ${2:-Run command}"
    echo -e ">> $1\\n"
    eval "$1"
}

libthemis_install_cmd_sudo() {
    libthemis_install_cmd "${cmd_sudo}${1}" "${2}${msg_sudo}"
}

libthemis_install_required_packages() {
    case "$os" in
        (debian|ubuntu)
                libthemis_install_cmd_sudo \
                    "apt-get update" \
                    "Update APT"
                libthemis_install_cmd_sudo \
                    "apt-get${flag_yes} install apt-transport-https wget" \
                    "Install required packages"
            ;;
        (centos)
                libthemis_install_cmd_sudo \
                    "${cmd_pkg}${flag_yes} makecache" \
                    "Update ${cmd_pkg} cache"
                libthemis_install_cmd_sudo \
                    "${cmd_pkg}${flag_yes} install wget" \
                    "Install required packages"
            ;;
        (*)
            libthemis_install_raise "can not install package for this OS."
            ;;
    esac

}

libthemis_install_from_package() {
    if [[ -z $flag_skip_install_required ]]; then
        libthemis_install_required_packages
    fi

    packages="libthemis"
    if [[ -n $flag_with_php ]]; then
        php_version=$(php --version | grep -Po "^PHP\s+\K\d+\.\d+") || \
            libthemis_install_raise "PHP is required for libphpthemis-php."
        packages+=" libphpthemis-php${php_version}";
    fi
    if [[ -n $flag_with_jni ]]; then
        packages+=' libthemis-jni';
    fi

    case "$os" in
        (debian|ubuntu)
            libthemis_install_cmd \
                "wget -qO - https://pkgs-ce.cossacklabs.com/gpg | "\
"${cmd_sudo}apt-key add -" \
                "Fetch Cossack Labs GPG key${msg_sudo}"
            libthemis_install_cmd \
                "echo \"deb https://pkgs-ce.cossacklabs.com/${branch}/${os} "\
"${os_ver_name} main\" | (umask 0077; "\
"${cmd_sudo}tee /etc/apt/sources.list.d/cossacklabs-ce.list)" \
                "Add Cossack Labs CE repository"
            libthemis_install_cmd_sudo \
                "apt-get update" \
                "Update APT"
            if [[ -z $flag_wo_dev ]]; then
                packages+=" libthemis-dev libthemispp-dev"
            fi
            libthemis_install_cmd_sudo \
                "apt-get${flag_yes} install ${packages}" \
                "Install libthemis"
            ;;
        (centos)
            libthemis_install_cmd_sudo \
                "rpm --import https://pkgs-ce.cossacklabs.com/gpg" \
                "Fetch Cossack Labs GPG key"
            libthemis_install_cmd \
                "wget -qO - https://pkgs-ce.cossacklabs.com/${branch}/centos/"\
"cossacklabs.repo |  (umask 0077; "\
"${cmd_sudo}tee /etc/yum.repos.d/cossacklabs-ce.repo)" \
                "Add Cossack Labs CE repository${msg_sudo}"
            cmd_makecache="${cmd_pkg}${flag_yes} makecache"
            if [[ "$os_ver_name" == '8' ]]; then
                # This is a workaround for the new weird behavior of yum/dnf
                # in CentOS 8 when adding a GPG key during the 'makecache'
                # command raises error code 141, despite the fact that
                # everything actually works well.
                cmd_makecache="/bin/bash -c 'set +e; ${cmd_makecache}; set -e'"
            fi
            libthemis_install_cmd_sudo \
                "$cmd_makecache" \
                "Update ${cmd_pkg} cache"
            if [[ -z $flag_wo_dev ]]; then
                packages+=" libthemis-devel libthemispp-devel"
            fi
            libthemis_install_cmd_sudo \
                "${cmd_pkg}${flag_yes} install ${packages}" \
                "Install libthemis"
            ;;
        (*)
            libthemis_install_raise "can not install package for this OS."
            ;;
    esac
}

libthemis_install_from_source() {
    if [[ -z $flag_skip_install_required ]]; then
        case "$os" in
            (debian|ubuntu)
                packages="git build-essential openssl libssl-dev"
                if [[ -z $flag_wo_packing ]]; then
                    packages+=" ruby ruby-dev rubygems lsb-release"
                fi
                libthemis_install_cmd_sudo \
                    "apt-get${flag_yes} install ${packages}" \
                    "Install development tools and required packages"
                ;;
            (centos)
                libthemis_install_cmd_sudo \
                    "${cmd_pkg}${flag_yes} groupinstall 'Development Tools'" \
                    "Install development tools"
                packages="git openssl openssl-devel"
                if [[ -z $flag_wo_packing ]]; then
                    packages+=" gcc make rpm-build rubygems "\
"libffi-devel ruby-devel"
                fi
                libthemis_install_cmd_sudo \
                    "${cmd_pkg}${flag_yes} install ${packages}" \
                    "Install required packages"
                ;;
            (*)
                libthemis_install_raise \
                    "can not install from sources for this OS."
                ;;
        esac

        if [[ -z $flag_wo_packing ]]; then
            if [[ "$os" != 'centos' ]]; then use_sudo="${cmd_sudo}"; fi
            libthemis_install_cmd \
                "${use_sudo}gem install --no-ri --no-rdoc fpm" \
                "Install fpm"
        fi
    fi

    opt_shallow=""
    if [[ -n $flag_wo_packing ]]; then
        opt_shallow=" --depth 1"
    fi
    libthemis_install_cmd \
        "git clone${opt_shallow} -b ${branch} "\
"https://github.com/cossacklabs/themis.git" \
        "Fetch libthemis sources"
    libthemis_install_cmd \
        "cd themis"
    libthemis_install_cmd \
        "make" \
        "Build libthemis"
    if [[ -n $flag_wo_packing ]]; then
        libthemis_install_cmd_sudo \
            "make install" \
            "Install libthemis"
        if [[ -n $flag_with_php ]]; then
            libthemis_install_cmd_sudo \
                "make phpthemis_install" \
                "Install libphpthemis"
        fi
        if [[ -n $flag_with_jni ]]; then
            libthemis_install_cmd_sudo \
                "make themis_jni_install" \
                "Install libthemis-jni"
        fi
        libthemis_install_cmd_sudo \
            "ldconfig" \
            "Rebuild system list of libraries"
    else
        case "$os" in
            (debian|ubuntu)
                libthemis_install_cmd \
                    "make deb" \
                    "Make deb packages"
                libthemis_install_cmd_sudo \
                    "dpkg -i ./build/deb/libthemis*.deb" \
                    "Install deb packages"
                ;;
            (centos)
                libthemis_install_cmd \
                    "make rpm" \
                    "Make rpm packages"
                libthemis_install_cmd_sudo \
                    "rpm -Uvh ./build/rpm/libthemis-*.rpm" \
                    "Install rpm packages"
                ;;
            (*)
                libthemis_install_raise "can not create package for this OS."
                ;;
        esac
    fi
    libthemis_install_cmd \
        "cd ${basedir}"
    if [[ -z $flag_wo_clean ]]; then
        libthemis_install_cmd \
            "rm -rf ${basedir}/themis" \
            "Remove source directory"
    fi
}

libthemis_install_main() {
    basedir=$(pwd)
    if [[ $(whoami) == 'root' ]]; then
        cmd_sudo=""
        msg_sudo=""
    else
        cmd_sudo="sudo "
        msg_sudo=", sudo password might be required"
    fi
    case "$method" in
        (package)
            libthemis_install_from_package
            ;;
        (source)
            libthemis_install_from_source
            ;;
        (*)
            libthemis_install_raise "unknown method '$method'."
            ;;
    esac
}

libthemis_install_post() {
    echo 'All done.'
}

libthemis_install() {
    libthemis_install_detect_os
    libthemis_install_os_consts
    libthemis_install_parse_args "$@"
    libthemis_install_check
    libthemis_install_main
    libthemis_install_post
}

libthemis_install "$@"
