#!/bin/bash

# Lib file with common functions for xivo-upgrade, xivocc-upgrade and mds-upgrade scripts

pre_stop() {
    local type="${1}"; shift

    /usr/bin/xivo-run-upgrade-scripts "${type}" pre-stop
}

post_stop() {
    local type="${1}"; shift

    /usr/bin/xivo-run-upgrade-scripts "${type}" post-stop
}

pre_start() {
    local type="${1}"; shift

    /usr/bin/xivo-run-upgrade-scripts "${type}" pre-start
}
post_start() {
    local type="${1}"; shift

    /usr/bin/xivo-run-upgrade-scripts "${type}" post-start
}

color_diff_between_versions() {
    local installed_version="$1"; shift
    local proposed_update="$1"; shift
    local i c1 c2

    local len=${#installed_version}
    # Take longest length
    (( ${#proposed_update} > len )) && len=${#proposed_update}

    local proposed_update_with_diff_colored=""

    for ((i=0; i<len; i++)); do
        c1="${installed_version:i:1}"
        c2="${proposed_update:i:1}"

        # If one string is shorter, treat missing char as space
        [[ -z "$c1" ]] && c1=" "
        [[ -z "$c2" ]] && c2=" "

        if [[ "$c1" == "$c2" ]]; then
            proposed_update_with_diff_colored+="$c2"
        else
            proposed_update_with_diff_colored+="\033[31m$c2\033[0m"
        fi
    done

    echo "$proposed_update_with_diff_colored"
}


debian_version_installed() {
    cut -d '.' -f 1 /etc/debian_version
}

is_bullseye() {
    [ "$(debian_version_installed)" -eq 11 ]
}

is_bookworm() {
    [ "$(debian_version_installed)" -eq 12 ]
}

is_trixie() {
    [ "$(debian_version_installed)" -eq 13 ]
}

generate_new_deb_sources_list() {
    local distrib="$1"; shift
    local source_list_file="$1"; shift

    cat <<-EOF > $source_list_file
	deb http://deb.debian.org/debian/ $distrib main
	deb-src http://deb.debian.org/debian/ $distrib main

	deb http://security.debian.org/debian-security/ $distrib-security main
	deb-src http://security.debian.org/debian-security/ $distrib-security main

	# $distrib-updates, previously known as 'volatile'
	deb http://deb.debian.org/debian/ $distrib-updates main
	deb-src http://deb.debian.org/debian/ $distrib-updates main
	EOF

}

deactivate_apt_sources_list() {
    for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
        [ -e "$f" ] && mv -f "$f" "$f.save"
    done
}

rollback_apt_sources_list() {
    for f in /etc/apt/sources.list.save /etc/apt/sources.list.d/*.list.save /etc/apt/sources.list.d/*.sources.save; do
        [ -e "$f" ] &&  mv -f "$f" "$(dirname $f)/$(basename $f '.save')"
    done
}

remove_deactivated_sources_list() {
    # Remove sources list files backedup before system upgrade
    # We only remove "our" sources list
    rm -f /etc/apt/sources.list.d/xivo-dist.list.save
    rm -f /etc/apt/sources.list.d/docker.list.save
    rm -f /etc/apt/sources.list.d/docker.sources.save
    rm -f /etc/apt/sources.list.d/pgdg.list.save
}

switch_to_trixie() {
    local distrib="trixie"

    # Deactivate all apt sources list
    deactivate_apt_sources_list

    # Generate default debian sources list
    generate_new_deb_sources_list "${distrib}" "/etc/apt/sources.list"

    # Add xivo related list:
    # - Reactivate xivo-dist
    mv -f "/etc/apt/sources.list.d/xivo-dist.list.save" "/etc/apt/sources.list.d/xivo-dist.list"

    # - Add pgdg list
    add_postgresql_sources_list "${distrib}"
}

add_postgresql_sources_list() {
    local distribution="${1:-trixie}"; shift

	cat > /etc/apt/sources.list.d/pgdg.list <<-EOF
	deb http://apt.postgresql.org/pub/repos/apt/ ${distribution}-pgdg main
	EOF

    add_key_in_keyring "https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc" "/etc/apt/trusted.gpg.d/apt.postgresql.org.gpg"
}

add_xivo_apt_conf() {
    if [ -f /etc/apt/apt.conf.d/90pf ]; then
        echo "Deleting old apt configuration file: etc/apt/apt.conf.d/90pf"
        rm -f /etc/apt/apt.conf.d/90pf
    fi
    echo "Installing new apt configuration file: /etc/apt/apt.conf.d/90xivo"

	cat > /etc/apt/apt.conf.d/90xivo <<-EOF
	Aptitude::Recommends-Important "false";
	APT::Install-Recommends "false";
	APT::Install-Suggests "false";
	EOF
}

check_source_exists() {
    local type="$1"; shift
    local distribution="$1"; shift
    local component="$1"; shift
    local list="$1"; shift

    grep -Prq "^\s*${type}\s.+\s${distribution}\s+${component}\b" ${list}
}

add_distribution_sources_list() {
    local distribution="${1:-trixie-backports}"

    if ! check_source_exists deb "${distribution}" main "/etc/apt/sources.list*"; then
            cat >> /etc/apt/sources.list <<-EOF

			deb http://ftp.fr.debian.org/debian ${distribution} main
			EOF
    fi
    if ! check_source_exists deb-src "${distribution}" main "/etc/apt/sources.list*"; then
            cat >> /etc/apt/sources.list <<-EOF
			deb-src http://ftp.fr.debian.org/debian ${distribution} main
			EOF
    fi
}

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

    if [ "${force}" -eq 0 ]; then
        echo ""
        read -p 'Would you like to upgrade your system (all services will be restarted) [N/y]? ' answer
        answer="${answer:-N}"
        if [ "$answer" != 'y' ] && [ "$answer" != 'Y' ]; then
            exit
        fi
    fi
}

display_upgrade_notice() {
    local notice="${1}"; shift
    local prefix="${1:-Upgrade}"; shift

    local message="${prefix}: ${notice}"
    printf -v border '%*s' "${#message}"


    echo ""
    echo "${border// /*}"
    echo "${message}"
    echo "${border// /*}"
    echo ""
}

display_system_upgrade_notice() {
	cat <<-EOF
	****************************************************************************
	*                                                                          *
	*  The Debian GNU/Linux system will be upgraded                            *
	*    from version 12 (Bookworm).                                           *
	*    to   version 13 (Trixie)                                              *
	*  Hence, this upgrade will be longer than other upgrades.                 *
	*                                                                          *
	*  You will need to RESTART the machine after the upgrade.                 *
	*                                                                          *
	****************************************************************************
	EOF
}

display_sources_list_notice() {
	cat <<-EOF
	****************************************************************************
	*                                                                          *
	*  Before upgrading to Debian 13 all apt sources list were deactivated.    *
	*  Check '*.list.save' files in the /etc/apt and /etc/apt/sources.list.d   *
	*  directory:                                                              *
	*      find /etc/apt -name  '*list.save'                                   *
	*                                                                          *
	*  Reactivate what is needed and check if it is compatible with Debian 13  *
	*                                                                          *
	****************************************************************************
	EOF
}

display_system_upgrade_reboot_notice() {
	cat <<-EOF
	**********************************************************
	*  WARNING: the system was upgraded                      *
	*    from Debian 12 (Bookworm)                           *
	*    to   Debian 13 (Trixie)                             *
	*                                                        *
	*  You must RESTART the machine to finalize the upgrade  *
	*                                                        *
	**********************************************************
	EOF
}

display_system_upgrade_failure() {
	cat <<-EOF
	**********************************************************
	*  WARNING: the system was not correctly upgraded        *
	*                                                        *
	**********************************************************
	EOF
}

display_xivocc_installer_log_notice() {
	cat <<-EOF
	**********************************************************
	*  XiVOCC INSTALLER LOG SUMMARY                          *
	*                                                        *
	*  The following log lines summarize the automatic       *
	*  operations performed by the xivocc-installer          *
	*  during the upgrade process.                           *
	*                                                        *
	*  Please carefully read the log below.                  *
	*  If any manual actions are mentioned,                  *
	*  you MUST perform them before proceeding.              *
	*                                                        *
	*  You can review the full log file at:                  *
	*    /var/log/xivocc-installer.log                       *
	**********************************************************
	EOF
}

is_grub_broken() {
    if [ -f /boot/grub/device.map ]; then
        for disk in $(awk '{print $2}' /boot/grub/device.map) ; do
            if [ ! -e "$disk" ] ; then
                return 0
            fi
        done
    fi
    install_device=$(debconf-show grub-pc | grep 'grub-pc/install_devices:' | cut -b3- | cut -f2 -d\ )
    if [ "$install_device" ] && [ ! -e "$install_device" ]; then
        return 0
    fi
    return 1
}

check_if_grub_is_broken() {
    if is_grub_broken; then
		cat <<-EOF
		*********************************************
		* You must install GRUB BEFORE rebooting:   *
		*                                           *
		* # apt install grub-pc                 *
		* # rm /boot/grub/device.map                *
		* # grub-install /dev/<boot_device>         *
		* # shutdown -r now                         *
		*                                           *
		* If you are often getting this error, you  *
		* should run "dpkg-reconfigure grub-pc".    *
		*                                           *
		*********************************************
		EOF
    fi
}

check_used_postgres() {
    local type="${1}"; shift
    local container_id dbname

    if [[ "$type" == "xivo" || "$type" == "mds" ]]; then
        local type="xivo"
        local dbname="db"
    else
        local type="xivocc"
        local dbname="pgxivocc"
    fi

    container_id=$(${type}-dcomp ps -aq "${dbname}" 2>/dev/null)
    if [ -n "$container_id" ]; then
        USE_DB=true
    else
        USE_DB=false
    fi
}

is_postgres_running_on_port() {
    local pgpass="${1:-proformatique}"; shift
    local dbhost="${1:-localhost}"; shift
    local dbport="${1:-5432}"; shift
    local dbusername="${1:-postgres}"; shift
    local dbname="${1:-asterisk}"; shift

    result=$(PGPASSWORD=${pgpass} psql -h "${dbhost}" -p ${dbport} -U ${dbusername} -qAtc "select 1 from pg_catalog.pg_database WHERE datname='${dbname}'" 2>/dev/null)

    [ $? -eq 0 ] && [ "$result" = "1" ]
}

wait_for_postgres() {
    local pgpass="${1:-proformatique}"; shift
    local dbhost="${1:-localhost}"; shift
    local dbport="${1:-5432}"; shift
    local dbusername="${1:-postgres}"; shift
    local dbname="${1:-asterisk}"; shift

    echo "Waiting for database $dbname on port $dbport."
    echo -n "It should not take more than a few minutes..."

    while true; do
        if is_postgres_running_on_port "$pgpass" "$dbhost" "$dbport" "$dbusername" "$dbname"; then
            break
        else
            sleep 1
            echo -n "."
        fi
    done
    echo
}

is_xivoucaddon() {
    [ -f /var/lib/xivo/uc_enabled ]
}

is_key_in_keyring() {
    local keyid="${1}"; shift
    local keyringfile="${1}"; shift

    if gpg --batch --no-tty --keyring "${keyringfile}" --list-keys --with-colons | grep -q "${keyid}"; then
        return 0
    else
        return 1
    fi
}

add_key_in_keyring() {
    local keyurl="${1}"; shift
    local keyringfile="${1}"; shift

    echo "Removing file ${keyringfile}"
    rm -rf "${keyringfile}"
    touch "${keyringfile}"
    echo "Adding GPG key from ${keyurl} to ${keyringfile}..."
    curl -fsSL "${keyurl}" | gpg --dearmor | tee "${keyringfile}" >/dev/null
}

remove_key_from_apt_keyring() {
    local keyid=${1}; shift

    echo "Removing key ${keyid} from /etc/apt/trusted.gpg"
    apt-key del "${keyid}"
}

check_enough_space_for_boot_partition() {
    # Ref.: https://www.debian.org/releases/trixie/release-notes/issues.fr.html#ensure-boot-has-enough-free-space
    local boot_partition_min_size=768 # at least a 768M size is recommended
    local boot_partition_min_freespace=300 # and 300M of freespace is needed

    local boot_mount_point boot_partition_size boot_partition_freespace
    local error_type=0

    if ! boot_mount_point=$(df /boot --output=target |tail -n +2); then
        error_type=99
        echo -e "\e[1;31m ERROR: could not retrieve /boot mount point\e[0m"
        exit 1
    fi

    if [ "${boot_mount_point}" = "/boot" ]; then
        # /boot is on its own mount point; checking size and avail space
        boot_partition_size=$(df /boot --output=size --block-size=M |tail -n +2 |tr -d 'M[:space:]')
        boot_partition_freespace=$(df /boot --output=avail --block-size=M |tail -n +2 |tr -d 'M[:space:]')
        # shellcheck disable=SC2086
        if [ ${boot_partition_size} -lt ${boot_partition_min_size} ]; then
            error_type=1
            echo -e "\e[1;33m WARNING: /boot partition size (${boot_partition_size}M) is lower than ${boot_partition_min_size}M\e[0m"
        fi
        # shellcheck disable=SC2086
        if [ ${boot_partition_freespace} -lt ${boot_partition_min_freespace} ]; then
            error_type=2
            echo -e "\e[1;33m WARNING: /boot partition freespace (${boot_partition_freespace}M) is lower than ${boot_partition_min_freespace}M\e[0m"
        fi
    fi
    
    if [ $error_type -ne 0 ]; then
        cat <<-EOF
		***********************************************************************************************************
		* WARNING: Upgrade aborted. You MUST fix /boot partition size or freespace.                               *
		*                                                                                                         *
		* Please refer to Debian Trixie (13) Release Notes:                                                       *
		*   https://www.debian.org/releases/trixie/release-notes/issues.fr.html#ensure-boot-has-enough-free-space *
		*                                                                                                         *
		***********************************************************************************************************
		EOF
        exit 1
    else
        echo ""
        echo "Checking /boot size requirements: ...OK"
        echo ""
    fi  
}

check_potential_interface_rename() {
    # Ref.: https://www.debian.org/releases/trixie/release-notes/issues.en.html#network-interface-names-may-change
    local net_iface net_iface_name
    local iface_rename=0

    echo ""
    echo "Checking potential network interface name change:"

    # Check whether predictable name was deactivated
    if grep -q "net.ifnames=0" /proc/cmdline; then
        echo "  So-called 'Predictable Name' is deactivated per kernel cmdline (net.ifnames=0)."
        echo "  Skipping this check as unrelevant."
        return 0
    fi

    for net_iface in /sys/class/net/*; do
        net_iface_name=$(basename "${net_iface}")

        # Skip virtual interface
        if readlink -f "${net_iface}" | grep -q "virtual"; then
            continue
        fi
        echo -n "  ${net_iface_name} ... "

        # Check potential name change
        net_iface_new_name=$(udevadm test-builtin net_setup_link "${net_iface}" 2>/dev/null| grep -oP 'ID_NET_NAME=\K.*')

        if [ "${net_iface_name}" = "${net_iface_new_name}" ]; then
            echo "OK (won't be renamed)"
        else
            # The check indicates that iface name will be renamed
            # But this might still be a false positive as interface name might be renamed via legacy udev rules
            if grep -q "${net_iface_name}" /etc/udev/rules.d/70-persistent-net.rules; then
                iface_rename=1
                echo -e "\e[1;33m WARNING: might be renamed to ${net_iface_new_name} but name should be kept via legacy udev rules\e[0m"
                echo "    Interface ${net_iface_name} is present in /etc/udev/rules.d/70-persistent-net.rules therefore it should not be renamed."
                echo -e "    \e[1;31mBEFORE REBOOTING\e[0m ensure this is actually true by checking this udev rules file."
                echo "    You might also consider to migrate legacy /etc/udev/rules.d/70-persistent-net.rules to a systemd .link files."
            else
                iface_rename=2
                echo -e "\e[1;31m NOK! ALERT: will be renamed to ${net_iface_new_name}\e[0m"
                echo -e "    \e[1;31mBEFORE REBOOTING\e[0m create a systemd .link file for this interface in order to ensure stable name"
                echo -e "\e[1;31m     If not done it will be impossible to reconnect via SSH after reboot\e[0m"
            fi
        fi
    done

    if [ $iface_rename -ne 0 ]; then
        display_warning_about_iface_rename
    fi
}

display_warning_about_iface_rename() {
    echo ""
    echo "  Find an example of systemd .link file here:"
    echo "    https://wiki.debian.org/NetworkInterfaceNames#CUSTOM_SCHEMES_USING_.LINK_FILES"
    echo "  For futher information refer to:"
    echo "    https://www.debian.org/releases/trixie/release-notes/issues.en.html#network-interface-names-may-change"
    echo ""
}