#!/bin/bash

PATH=/bin:/usr/bin:/sbin:/usr/sbin
: ${XIVO_CONFD_PORT:='9486'}
xivo_version_file="/var/lib/xivo/xivo_previous_version"

source /usr/bin/xivo-upgrade-functions

execute() {
    cmd=$*
    $cmd
    if [ $? -ne 0 ]; then
        start_xivo
        exit 1
    fi
}

stop_xivo() {
    xivo-service stop
    xivo-service disable
}

start_xivo() {
    xivo-service enable
    xivo-service start
}

clean_tokens_from_consul() {
    echo "Cleaning tokens from consul"
    /etc/init.d/consul stop
    rm -rf /var/lib/consul/*
    dpkg-reconfigure consul
}

remove_rabbitmq_anonymous_volume() {
    echo "Cleaning rabbitmq configuration and data"
    xivo-dcomp stop rabbitmq
    xivo-dcomp rm -f rabbitmq > /dev/null
}

upgrade_rabbitmq() {
    remove_rabbitmq_anonymous_volume
    # We need to clean tokens from consul otherwise they will stay forever (because we cleaned rabbitmq memory)
    clean_tokens_from_consul
    xivo-dcomp pull rabbitmq
    xivo-dcomp up -d rabbitmq
}

export_xivo_version_to_env_and_file() {
    export XIVO_VERSION_INSTALLED=$(get_xivo_version_installed_from_apt)
    export XIVO_VERSION_CANDIDATE=$(get_xivo_version_candidate_from_apt)
    echo "$XIVO_VERSION_INSTALLED" > $xivo_version_file
}

xivo_upgrade() {
    # Docker installation preparation
    install_xivo_docker
    apt update

    pre_stop "xivo"
    stop_xivo
    post_stop "xivo"
    echo "Upgrading xivo..."
    if is_buster; then
        check_active_db_conn
        export UPGRADING_TO_BULLSEYE=1
        exec_buster_to_bullseye_migration
    else
        echo "Executing manual upgrade actions..."
        if dpkg --compare-versions "$XIVO_VERSION_INSTALLED" "<<" "2022.10.02"; then
            execute apt-get install -y -o Dpkg::Options::="--force-confnew" xivo-docker-components
            upgrade_rabbitmq
        fi

        echo "Executing XiVO PBX upgrade actions..."
        execute apt dist-upgrade -y -o Dpkg::Options::="--force-confnew"

        echo "Executing cleaning actions..."
        apt autoremove -y
    fi

    echo "Removing obsolete dahdi-linux-modules-* packages..."
    apt purge '^dahdi-linux-modules*' -y

    echo "Running post-upgrade actions..."
    real-xivo-finish-upgrade
    if [[ "$UPGRADING_TO_BULLSEYE" == "1" ]]; then
        check_if_grub_is_broken
        display_buster_to_bullseye_upgrade_reboot_notice
    fi
}

display_xivo_version() {
    echo "installed version : $XIVO_VERSION_INSTALLED"
    echo "proposed update	: $XIVO_VERSION_CANDIDATE"
}

recreate_upgrade_status_file_and_export() {
    local status_file="${1}"; shift

    rm -f "${status_file}"
    touch "${status_file}"
    export XIVO_UPGRADE_STATUS_FILE=${status_file}
}

upgrading_system() {
    local force="${1}"; shift

    prepare_package_sources
    display_xivo_version
    if is_buster; then
        display_system_upgrade_notice
    fi
    ask_to_continue_upgrade "${force}"
    local xivo_upgrade_status_file="/tmp/xivo-upgrade.status"
    recreate_upgrade_status_file_and_export "${xivo_upgrade_status_file}"

    xivo_upgrade
}

is_linux_image_amd64_meta_installed() {
    [ -n "$(dpkg -l linux-image-amd64 2>/dev/null | grep '^ii')" ]
}

is_linux_image_686_pae_meta_installed() {
    [ -n "$(dpkg -l linux-image-686-pae 2>/dev/null | grep '^ii')" ]
}

is_linux_image_586_meta_installed() {
    [ -n "$(dpkg -l linux-image-586 2>/dev/null | grep '^ii')" ]
}

is_cpu_support_pae() {
    grep -q pae /proc/cpuinfo
}

install_linux_image() {
    case $(dpkg --print-architecture) in
        amd64)
            if ! is_linux_image_amd64_meta_installed ; then
                apt install --yes linux-image-amd64
            fi
            ;;
        i386)
            if is_cpu_support_pae ; then
                if ! is_linux_image_686_pae_meta_installed ; then
                    apt install --yes linux-image-686-pae
                fi
            else
                if ! is_linux_image_586_meta_installed ; then
                    apt install --yes linux-image-586
                fi
            fi
            ;;
    esac
}

is_there_active_db_conn() {
    res=$(sudo -u postgres psql -Aqt -c "SELECT COUNT(*) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND state = 'active';" 2> /dev/null)
    if [ $? -ne 0 ]; then
		cat <<-EOF
		***************************************************************
		* ERROR:  there was an error during connection to postgresql. *
		* Upgrade aborted.                                            *
		* Check your installation an re-launch the upgrade.           *
		***************************************************************
		EOF
        exit 1
    else
        return ${res}
    fi
}

check_active_db_conn() {
    if ! is_there_active_db_conn ; then
		cat <<-EOF
		***************************************************************************************************************************
		* WARNING: There are some unexpected active connections to the database.                                                  *
		*          Upgrade aborted.                                                                                               *
		*                                                                                                                         *
		* You MUST:                                                                                                               *
		*   1. close these unexpected active database connections,                                                                *
		*   2. and then, finish this upgrade with xivo-upgrade                                                                    *
		*                                                                                                                         *
		* 1. To find these spurious connections to the database :                                                                 *
		*   - Stop xivo services                                                                                                  *
		*     # xivo-service stop                                                                                                 *
		*   - List active connections                                                                                             *
		*     # sudo -u postgres psql -c "SELECT * FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND state = 'active';"     *
		*     or                                                                                                                  *
		*     # ss -anp|grep [:\.]5432|grep -v LISTEN                                                                             *
		*   - Stop these connections and ensure that they won't re-connect                                                        *
		* 2. Re-run the upgrade :                                                                                                 *
		*     # xivo-service start                                                                                                *
		*     # xivo-upgrade                                                                                                      *
		***************************************************************************************************************************
		EOF
        exit 1
    fi
}

# Stretch installed from PXE may have a broken vimrc.local
fix_vimrc_local_for_pxe_install() {
    local vimrc_local_file="/etc/vim/vimrc.local"
    if [ -e "${vimrc_local_file}" ]; then
        # shellcheck disable=SC2016
        sed -i 's#source /usr/share/vim/vim80/defaults.vim#source $VIMRUNTIME/defaults.vim#' "${vimrc_local_file}"
    fi
}

exec_buster_to_bullseye_migration() {
    local force_yes="--allow-downgrades --allow-remove-essential --allow-change-held-packages"

    add_xivo_apt_conf

    echo "Executing pre-bullseye upgrade actions..."
    # Force install of new dahdi-linux-dkms module before switching to bullseye
    apt install --yes ${force_yes} -o Dpkg::Options::="--force-confnew" dahdi-linux-dkms

    # Switch to Bullseye
    switch_to_bullseye

    apt update

    echo "Executing manual upgrade actions..."
    # Upgrade docker-ce before xivo upgrade (force bullseye repo)
    # shellcheck disable=SC2086
    apt install --yes ${force_yes} -o Dpkg::Options::="--force-confnew" docker-ce/bullseye docker-ce-cli/bullseye containerd.io/bullseye
    # Docker upgrade starts the containers that were stopped at beginning of upgrade.
    # So stopping them again
    xivo-service stop

    # Do not force-rewrite factory.env file
    # shellcheck disable=SC2086
    apt-get install --yes ${force_yes} xivo-docker-components
    upgrade_rabbitmq

    apt update
    apt purge xivo-ctid --yes

    echo "Executing XiVO PBX full upgrade actions..."
    # shellcheck disable=SC2086
    apt dist-upgrade --yes ${force_yes} -o Dpkg::Options::="--force-confnew"

    # Fixing upgrade: set manually XIVO_UUID and run apt -f to fix xivo-confd/xivo-base/xivo postinst
    echo "Set the XIVO_UUID at the end of the upgrade..."
    xivo-configure-uuid
    apt -f install --yes

    apt update
    echo "Executing cleaning actions..."
    apt autoremove --yes

    install_linux_image
    fix_vimrc_local_for_pxe_install
}

list_packages_with_filter() {
    local filter="$1"
    aptitude -q -F '%p' --disable-columns search "$filter"
}

prepare_package_sources () {
    install_xivo_dist
}

install_xivo_docker() {
    apt install --yes xivo-docker
}

install_xivo_dist() {
    is_dist_installed=$(list_packages_with_filter "?installed?name(\"^xivo-dist$\")")
    if [ -z "$is_dist_installed" ]; then
        echo "Installing xivo-dist package..."
        if ! apt install --yes xivo-dist; then
            echo "Failed to install xivo-dist. Aborting upgrade."
            exit 1
        fi
    fi
}

xivo_package() {
    list_packages_with_filter "?installed?name(\"^xivo$|^xivo-base$|^pf-xivo$\")" | head -n1
}

get_xivo_version_installed_from_apt() {
    echo "$(LANG='C' apt-cache policy $(xivo_package) | grep Installed | grep -oE '[0-9]{2,4}\.[0-9]+(\.[0-9]+)?|1\.2\.[0-9]{1,2}' | head -n1)"
}

get_xivo_version_candidate_from_apt() {
    echo "$(LANG='C' apt-cache policy xivo | grep Candidate | grep -oE '[0-9]{2,4}\.[0-9]+(\.[0-9]+)?|1\.2\.[0-9]{1,2}' | head -n1)"
}

check_64bit_system() {
    machine=$(uname -m)
    if [ "$machine" = "i686" ]; then
		cat <<-EOF
		**********************************************************************
		* You are trying to upgrade to version that requires 64-bit system.  *
		* Upgrade aborted.                                                   *
		*                                                                    *
		* Please revert the sources list and downgrade the xivo-upgrade      *
		* package to the latest LTS version for 32-bit systems :             *
		* # xivo-dist xivo-polaris                                           *
		* # apt update                                                   *
		* # apt policy xivo-upgrade                                    *
		* # apt install xivo-upgrade=<AVAILABLE POLARIS VERSION>         *
		*                                                                    *
		**********************************************************************
		EOF
        exit 1
    fi
}

check_wizard_has_been_run() {
    if [[ "$XIVO_VERSION_INSTALLED" > "16.08.2" && "$XIVO_VERSION_INSTALLED" < "2016" ]]; then
        echo "Your version is $XIVO_VERSION_INSTALLED. Unfortunately, there are no more upgrades for versions above 16.08.2."
        echo "Please contact XiVO (https://xivo.solutions) for support."
        exit 1
    fi

    if ! is_xivo_configured; then
        echo "ERROR: You must configure XiVO by running the web wizard before using xivo-upgrade"
        exit 1
    fi
}

is_xivo_configured() {
    if [ -f "/var/lib/xivo/configured" ]; then
        return 0
    fi

    if [ ! -f "/var/run/xivo-confd/xivo-confd.pid" ]; then
        echo "ERROR: xivo-confd is not started. Cannot check if Wizard was run."
        echo "Start xivo-confd and relaunch upgrade"
        exit 1
    fi

    [ -n "$(curl -skX GET --header 'Accept: application/json' "https://localhost:$XIVO_CONFD_PORT/1.1/wizard" | grep '\"configured\"[[:space:]]*:[[:space:]]*true')" ]
}

usage() {
    local progname="${1}"; shift

    echo "usage: ${progname} [-d] [-f] [-h]"
    echo -e "\t-d: only download packages"
    echo -e "\t-f: force install the proposed version"
    echo -e "\t-h: print usage"
}

main() {
    local progname="xivo-upgrade"
    # Parse flags
    local download_only
    local force
    while getopts :dfh opt
    do
        case ${opt} in
            d) download_only=1;;
            f) force=1;;
            h)
                usage "${progname}"
                exit 0
            ;;
            '?')
                echo "${progname} : option ${OPTARG} is not valid" >&2
                usage "${progname}"
                exit 1
            ;;
        esac
    done
    download_only="${download_only:-"0"}"
    force="${force:-"0"}"

    # Beginning
    export DEBIAN_FRONTEND=noninteractive
    export APT_LISTCHANGES_FRONTEND=none

    check_64bit_system
    export_xivo_version_to_env_and_file
    check_wizard_has_been_run
    if (! is_buster && ! is_bullseye); then
        echo "Upgrade aborted."
        echo "Your XiVO is lower than Freya (2020.18) and uses Debian 9"
        echo "Upgrade to XiVO >= 2022.04 (which uses Debian 11) is not supported"
        echo "You MUST first upgrade to a XiVO >= Freya (2020.18)"
        exit 1
    fi

    if [ $download_only -eq 0 ]; then
        upgrading_system "${force}"
    else
        trap : SIGINT
        if is_buster; then
            # Switch repo to bullseye
            switch_to_bullseye
            apt update

            # Download packages
            apt -y -d -o Dpkg::Options::="--force-confnew" dist-upgrade

            # Switch back to buster
            change_sources_list 'bullseye' 'buster' '/etc/apt/sources.list'
            change_sources_list 'bullseye' 'buster' '/etc/apt/sources.list.d/docker.list'
            add_postgresql_sources_list 'buster'
            # Fix the security repo name change
            change_sources_list 'buster-security' 'buster\/updates' '/etc/apt/sources.list'
            # Ensure buster backports are present
            add_distribution_sources_list 'buster-backports'
            apt update
        else
            apt -y -d -o Dpkg::Options::="--force-confnew" dist-upgrade
        fi
    fi
}

main "${@}"