#!/bin/bash

export PATH="${PATH}:/bin:/sbin:/usr/bin:/usr/sbin"

readonly AEMBIT_GROUP_NAME="aembit"
readonly AEMBIT_AGENT_CONTROLLER_USER_NAME="aembit_agent_controller"
readonly AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT="aembit_agent_controller.service"
readonly AEMBIT_AGENT_CONTROLLER_VERSION="1.27.2904"
readonly AEMBIT_BASE_INSTALL_DIR="/opt/aembit"
readonly AEMBIT_AGENT_CONTROLLER_INSTALL_DIR="${AEMBIT_BASE_INSTALL_DIR}/edge/agent_controller"
readonly AEMBIT_AGENT_CONTROLLER_SECRETS_FILENAME=".credentials.json"
readonly AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN="${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}"/"${AEMBIT_AGENT_CONTROLLER_VERSION}"/bin
readonly AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_SCRIPTS="${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}"/"${AEMBIT_AGENT_CONTROLLER_VERSION}"/scripts
readonly AEMBIT_AGENT_CONTROLLER_JOURNALD_CONFIG_FILE="/etc/systemd/journald@aembit_agent_controller.conf"
readonly AEMBIT_AGENT_CONTROLLER_VERSION_PATTERN="[[:digit:]]*.[[:digit:]]*.[[:digit:]]*"
readonly AEMBIT_AGENT_CONTROLLER_BIN="aembit_agent_controller"

INSTALLER_DIR=$(dirname "${0}")
readonly LOG_FILE="${INSTALLER_DIR}"/installer.log

readonly SYSTEMD_UNIT_FILES_FOLDER="/etc/systemd/system"
readonly MIN_SYSTEMD_VERSION_WITH_NAMESPACE_JOURNALS=245
readonly SELINUX_GENERATE_POLICY_SCRIPT="${INSTALLER_DIR}"/selinux/generate_selinux_policy.sh

# OPTIONS
TO_UPGRADE=false

parse_options() {
    for _ in $(getopt --options "" --longoptions "upgrade" -- "${@}"); do
        case $1 in
            --upgrade)
                TO_UPGRADE=true ;;
        esac
        shift
    done
}

log() {
    local level="${1}"
    shift

    local log_fmt="%s %s\n"
    if [ "${level}" ]; then
        log_fmt="%s ${level} %s\n"
    fi

    for line in "$@"; do
        # shellcheck disable=SC2059
        printf "${log_fmt}" "$(date +"%H:%M:%S")" "${line}" | tee -a "${LOG_FILE}"
    done
}

log_info() {
    log "Info:" "$@"
}

log_warn() {
    log "Warning:" "$@"
}

log_err() {
    log "Error:" "$@"
}

# Exits 0 if the current platform is $1, else exits with 1.
#
# Examples:
# * is_platform rhel
# * is_platform ubuntu
# * is_platform "Red Hat Enterprise Linux"
# * is_platform Jammy
is_platform() {
    platform=$1
    val=$(cat /etc/*-release | grep -c "$platform")
    if [[ $val -gt 0 ]]; then
        return 0
    else
        return 1
    fi
}

check_user_file_access() {
    user=$1
    file_path=$2

    if sudo -u "$user" test -r "$file_path" && sudo -u "$user" test -x "$file_path"; then
        return 0
    else
        log_err "User '${user}' does not have access to ${file_path}"
        return 1
    fi
}

check_running_as_root() {
    if [ "$(id --user)" -ne 0 ]; then
        log_err "Installer must be run as root."
        exit 1
    fi
}

check_for_required_env_var() {
    env_var_name=$1
    env_var_value="${!env_var_name}"

    if [ -z "${env_var_value}" ]; then
        log_err "Required environment variable \"${env_var_name}\" is not defined."
        exit 1
    fi
}

check_registration_method() {
  if [[ -z "${AEMBIT_DEVICE_CODE}" && -z "${AEMBIT_AGENT_CONTROLLER_ID}" ]]; then
        log_err "Either environment variable \"AEMBIT_DEVICE_CODE\" or \"AEMBIT_AGENT_CONTROLLER_ID\" is required to be present for registration."
        exit 1
    fi
}

check_tls_methods_exclusivity() {
    # Either Aembit Managed TLS or Customer PKI-signed TLS certificates can be used.
    if [[ ("${TLS_PEM_PATH}" || "${TLS_KEY_PATH}") && "${AEMBIT_MANAGED_TLS_HOSTNAME}" ]]; then
        log_err "Only one TLS configuration method should be selected. Please choose either Aembit-managed TLS certificates (\"AEMBIT_MANAGED_TLS_HOSTNAME\") or customer PKI-signed TLS certificates (\"TLS_PEM_PATH\" and \"TLS_KEY_PATH\")."
        exit 1
    fi
}

set_default_stack_domain() {
    if [ -z "${AEMBIT_STACK_DOMAIN}" ]; then
        export AEMBIT_STACK_DOMAIN="useast2.aembit.io"
    fi
}

set_file_user_and_group_owner() {
    file_path=$1
    user=$2
    group=$3

    if ! chown "${user}:${group}" "${file_path}"; then
        log_err "Unable to set the ownership of ${file_path}."
        exit 1
    fi
}

set_file_permissions() {
    file_path=$1
    permissions=$2

    if ! chmod "${permissions}" "${file_path}"; then
        log_err "Unable to set the permissions of ${file_path}."
        exit 1
    fi
}

copy_tls_cert() {
    if [[ "${TLS_PEM_PATH}" && "${TLS_KEY_PATH}" ]]; then
        if [[ -f "${TLS_PEM_PATH}" && -f "${TLS_KEY_PATH}" ]]; then
            cp "${TLS_PEM_PATH}" "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.crt"
            cp "${TLS_KEY_PATH}" "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.key"
            set_file_user_and_group_owner "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.crt" "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" "${AEMBIT_GROUP_NAME}"
            set_file_user_and_group_owner "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.key" "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" "${AEMBIT_GROUP_NAME}"
            set_file_permissions "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.crt" 400
            set_file_permissions "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.key" 400
            log_info "Copied TLS pem and key."
        else
            log_info "TLS_PEM_PATH and/or TLS_KEY_PATH are not valid. Skipping custom-PKI TLS config."
        fi
    else
        log_info "Both TLS_PEM_PATH and TLS_KEY_PATH must be set for TLS. Skipping custom-PKI based TLS config."
    fi
}

check_if_package_missing() {
    package=$1

    if ! which "${package}" > /dev/null 2>&1; then
        if [ -z "${missing_packages}" ]; then
            missing_packages="${package}"
        else
            missing_packages="${missing_packages}, ${package}"
        fi
    fi
}

check_if_lib_missing() {
    lib=$1

    if ! ldconfig -p | grep "$lib" > /dev/null 2>&1; then
        if [ -z "${missing_packages}" ]; then
            missing_packages="${lib}"
        else
            missing_packages="${missing_packages}, ${lib}"
        fi
    fi
}

check_required_dependencies() {
    log_info "Checking for required package dependencies."

    check_if_package_missing pgrep
    check_if_package_missing grep
    check_if_package_missing id
    check_if_package_missing getent
    check_if_package_missing useradd
    check_if_package_missing groupadd
    check_if_lib_missing libicu

    if [ -n "${missing_packages}" ]; then
        log_err "One or more necessary packages were missing. Please install the following package(s) and then rerun the installer: ${missing_packages}"
        exit 1
    fi
}

agent_controller_is_already_installed() {
    if ! [ -d "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}" ]; then
        return 1
    fi

    for file in ${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/${AEMBIT_AGENT_CONTROLLER_VERSION_PATTERN}; do
        # If there is no match, $file just refers to the pattern, instead of a
        # file name. No file exists with the name of the pattern literal.
        if [ -e "${file}" ]; then
            return 0
        else
            return 1
        fi
    done
}

agent_controller_is_already_running() {
    if pgrep --euid "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" &>/dev/null; then
        return 0
    else
        return 1
    fi
}

# Whether the installer is being run in upgrade mode.
is_upgrade() {
    if [ "${TO_UPGRADE}" == "true" ]; then
        return 0
    else
        return 1
    fi
}

handle_existing_agent_controller() {
    if (agent_controller_is_already_running || agent_controller_is_already_installed) && ! is_upgrade; then
        log_err "Aembit Agent Controller is already installed. Please uninstall it first or use the \"--upgrade\" option."
        exit 1
    fi
}

# Sets the group owner and permissions for all directories from $base_dir to $leaf_dir, inclusive.
set_group_owner_and_permissions_in_range() {
    base_dir=$1
    leaf_dir=$2
    perms=$3
    group=$4

    current_dir="${leaf_dir}"
    while true; do
        chgrp "${group}" "${current_dir}"
        chmod "${perms}" "${current_dir}"
        if [ "${current_dir}" = "${base_dir}" ]; then
            break
        fi
        current_dir="$(dirname "${current_dir}")"
    done
}


set_recursive_file_permissions() {
    file_path=$1
    permissions=$2

    if ! chmod --recursive "${permissions}" "${file_path}"; then
        log_err "Unable to set the permissions of ${file_path}."
        exit 1
    fi
}

set_recursive_file_user_and_group_owner() {
    file_path=$1
    user=$2
    group=$3

    if ! chown --recursive "${user}:${group}" "${file_path}"; then
        log_err "Unable to set the ownership of ${file_path}."
        exit 1
    fi
}

create_user_and_group() {
    if getent group "${AEMBIT_GROUP_NAME}" > /dev/null 2>&1; then
        log_info "Group \"${AEMBIT_GROUP_NAME}\" exists."
    else
        log_info "Group \"${AEMBIT_GROUP_NAME}\" does not exist. Creating."
        if groupadd "${AEMBIT_GROUP_NAME}"; then
            log_info "Group created."
        else
            log_err "Error creating the \"${AEMBIT_GROUP_NAME}\" group."
            exit 1
        fi
    fi

    if getent passwd "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" > /dev/null 2>&1; then
        log_info "User \"${AEMBIT_AGENT_CONTROLLER_USER_NAME}\" exists."
    else
        log_info "User \"${AEMBIT_AGENT_CONTROLLER_USER_NAME}\" does not exist. Creating."
        if useradd --system --shell /bin/false  --gid "${AEMBIT_GROUP_NAME}" "${AEMBIT_AGENT_CONTROLLER_USER_NAME}"; then
            log_info "User created."
        else
            log_err "There was an error creating the \"${AEMBIT_AGENT_CONTROLLER_USER_NAME}\" user."
            exit 1
        fi
    fi
}

# get systemd version using systemctl
get_systemd_version() {
    systemd_version_output=$(systemctl --version)
    echo "$systemd_version_output" | grep -oP 'systemd \K\d+' | tr -dc '0-9'
}

set_up_logs() {

    # From "man systemd-journald.service"
    # "On systems where /var/log/journal/ does not exist yet but where persistent logging is
    #  desired (and the default journald.conf is used), it is sufficient to create the
    #  directory, and ensure it has the correct access modes and ownership:"
    if [ ! -d "/var/log/journal" ]; then
        mkdir -p /var/log/journal
	chgrp systemd-journal /var/log/journal
        if ! systemd-tmpfiles --create --prefix /var/log/journal; then
            exit 1
        fi
    fi

    if [ "$(get_systemd_version)" -ge "${MIN_SYSTEMD_VERSION_WITH_NAMESPACE_JOURNALS}" ]; then
        if ! cp installer_components/journald@aembit_agent_controller.conf "${AEMBIT_AGENT_CONTROLLER_JOURNALD_CONFIG_FILE}"; then
            log_err "Unable to copy the journald configuration file to host."
            exit 1
        fi

        set_file_permissions "${AEMBIT_AGENT_CONTROLLER_JOURNALD_CONFIG_FILE}" 400 || exit 1
        set_file_user_and_group_owner "${AEMBIT_AGENT_CONTROLLER_JOURNALD_CONFIG_FILE}" root root || exit 1
    fi
}

copy_static_components_to_host() {
    if ! mkdir --parents "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN}"; then
        log_err "Unable to make installation directory for Agent Controller binary."
        exit 1
    fi

    if ! cp "${AEMBIT_AGENT_CONTROLLER_BIN}" "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN}"/"${AEMBIT_AGENT_CONTROLLER_BIN}"; then
        log_err "Unable to move the Agent Controller binary to the installation directory."
        exit 1
    fi

    if ! cp appsettings.json "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN}"/appsettings.json; then
        log_err "Unable to move the Agent Controller configuration to the installation directory."
        exit 1
    fi

    if ! mkdir --parents "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_SCRIPTS}"; then
        log_err "Unable to make installation directory for Agent Controller scripts."
        exit 1
    fi

    if ! cp uninstall "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_SCRIPTS}"/uninstall; then
        log_err "Unable to move the Agent Controller uninstall script to the installation directory."
        exit 1
    fi

    # Ensure the installed files are accessible throughout the installation dir structure
    # (needed when installing into environments with very restrictive umasks).
    set_group_owner_and_permissions_in_range "${AEMBIT_BASE_INSTALL_DIR}" "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/.." 550 "${AEMBIT_GROUP_NAME}"
    set_recursive_file_permissions "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}" 500 || exit 1

    if ! echo "" > "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}"/"${AEMBIT_AGENT_CONTROLLER_SECRETS_FILENAME}"; then
	    log_err "Unable to create Agent Controller secrets file."
        exit 1
    fi

    set_file_permissions "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/${AEMBIT_AGENT_CONTROLLER_SECRETS_FILENAME}" 600 || exit 1
    set_recursive_file_user_and_group_owner "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}" "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" "${AEMBIT_GROUP_NAME}" || exit 1
}

install_systemd_service() {
    if ! cp installer_components/aembit_agent_controller.service "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to copy the systemd service file to the destination."
        exit 1
    fi

    set_file_permissions "${SYSTEMD_UNIT_FILES_FOLDER}/${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}" 400 || exit 1
    set_file_user_and_group_owner "${SYSTEMD_UNIT_FILES_FOLDER}/${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}" root root || exit 1

    if ! sed --in-place "s#{{ AEMBIT_TENANT_ID }}#${AEMBIT_TENANT_ID}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update the Agent Controller Tenant Id in the systemd service file."
        exit 1
    fi

    if ! sed --in-place "s#{{ AEMBIT_STACK_DOMAIN }}#${AEMBIT_STACK_DOMAIN}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update the Agent Controller Stack Domain in the systemd service file."
        exit 1
    fi

    if ! sed --in-place "s#{{ AEMBIT_DEVICE_CODE }}#${AEMBIT_DEVICE_CODE}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update the Agent Controller Device Code in the systemd service file."
        exit 1
    fi

    if ! sed --in-place "s#{{ AEMBIT_AGENT_CONTROLLER_ID }}#${AEMBIT_AGENT_CONTROLLER_ID}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update the Agent Controller Agent Controller Id in the systemd service file."
        exit 1
    fi

    # Aembit Managed Tls Setup
    optional_aembit_managed_tls_hostname=''
    if [ -n "${AEMBIT_MANAGED_TLS_HOSTNAME}" ]; then
        optional_aembit_managed_tls_hostname="Environment=AEMBIT_MANAGED_TLS_HOSTNAME=${AEMBIT_MANAGED_TLS_HOSTNAME}"
    else
        log_info "Skipping managed TLS hostname config."
    fi

    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_MANAGED_TLS_HOSTNAME }}#${optional_aembit_managed_tls_hostname}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update managed TLS hostname in the systemd service file."
        exit 1
    fi
    
    # Customer-managed TLS setup
    optional_tls_pem_path=''
    if [ -n "${TLS_PEM_PATH}" ]; then
        optional_tls_pem_path="Environment=TLS_PEM_PATH=${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.crt"
    else
        log_info "Skipping customer-managed TLS PEM path."
    fi

    if ! sed --in-place "s#{{ OPTIONAL_TLS_PEM_PATH }}#${optional_tls_pem_path}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update customer-managed TLS PEM path in the systemd service file."
        exit 1
    fi
    
    optional_tls_key_path=''
    if [ -n "${TLS_KEY_PATH}" ]; then
        optional_tls_key_path="Environment=TLS_KEY_PATH=${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/tls.key"
    else
        log_info "Skipping customer-managed TLS KEY path."
    fi

    if ! sed --in-place "s#{{ OPTIONAL_TLS_KEY_PATH }}#${optional_tls_key_path}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update customer-managed TLS key path in the systemd service file."
        exit 1
    fi

    # Add LogNamespace only if systemd version is 245 or greater (when LogNamespace was introduced).
    # Added in version 245.
    # See LogNamespace section in https://www.man7.org/linux/man-pages/man5/systemd.exec.5.html
    aembit_log_namespace_value=''
    if [ "$(get_systemd_version)" -ge "${MIN_SYSTEMD_VERSION_WITH_NAMESPACE_JOURNALS}" ]; then
        aembit_log_namespace_value="LogNamespace=$AEMBIT_AGENT_CONTROLLER_USER_NAME"
    fi

    # If systemd version is prior to 245, a blank line will be present in the unit file.
    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_LOG_NAMESPACE }}#${aembit_log_namespace_value}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional log namespace in the systemd service file."
        exit 1
    fi

    # Kerberos environment variables

    optional_aembit_kerberos_attestation_enabled=''
    if [ -n "${AEMBIT_KERBEROS_ATTESTATION_ENABLED}" ]; then
        flag_options=("true" "false")
        if [[ ${flag_options[*]} =~ ${AEMBIT_KERBEROS_ATTESTATION_ENABLED} ]]; then
            optional_aembit_kerberos_attestation_enabled="Environment=AEMBIT_KERBEROS_ATTESTATION_ENABLED=${AEMBIT_KERBEROS_ATTESTATION_ENABLED}"
        else
            log_info "Unable to set Kerberos Attestation flag. Available options; true, false."
        fi
    else
        log_info "Skipping Kerberos attestation config."
    fi

    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_KERBEROS_ATTESTATION_ENABLED }}#${optional_aembit_kerberos_attestation_enabled}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional Kerberos attestation enabled flag in the systemd service file."
        exit 1
    fi

    optional_krb5_ktname=''
    if [ -n "${KRB5_KTNAME}" ]; then
        optional_krb5_ktname="Environment=KRB5_KTNAME=${KRB5_KTNAME}"
    fi

    if ! sed --in-place "s#{{ OPTIONAL_KRB5_KTNAME }}#${optional_krb5_ktname}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional Kerberos keytab filename in the systemd service file."
        exit 1
    fi

    # Aembit Log Level Setup
    optional_log_level='Environment=AEMBIT_LOG_LEVEL=Warning'
    if [ -n "${AEMBIT_LOG_LEVEL}" ]; then
        LOG_LEVEL=$(echo "$AEMBIT_LOG_LEVEL" | tr '[:upper:]' '[:lower:]')
        log_levels=("fatal" "error" "warning" "information" "debug" "verbose" "info" "warn" "trace")
        if [[ " ${log_levels[*]} " == *" ${LOG_LEVEL} "* ]]; then
            optional_log_level="Environment=AEMBIT_LOG_LEVEL=${AEMBIT_LOG_LEVEL}"
        else
            log_err "Unable to set Log Level. Available options: fatal, error, warning, information, debug, verbose (synonyms: info, warn, trace)."
            exit 1
        fi
    fi

    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_LOG_LEVEL }}#${optional_log_level}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional log level in the systemd service file."
        exit 1
    fi

    # Aembit Metrics Enabled Setup
    optional_aembit_metrics_enabled=''
    if [ -n "${AEMBIT_METRICS_ENABLED}" ]; then
        optional_aembit_metrics_enabled="Environment=AEMBIT_METRICS_ENABLED=${AEMBIT_METRICS_ENABLED}"
    fi

    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_METRICS_ENABLED }}#${optional_aembit_metrics_enabled}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional metrics enabled in the systemd service file."
        exit 1
    fi
    
    # Aembit HTTP Port Disabled Setup
    optional_aembit_http_port_disabled=''
    if [ -n "${AEMBIT_HTTP_PORT_DISABLED}" ]; then
        optional_aembit_http_port_disabled="Environment=AEMBIT_HTTP_PORT_DISABLED=${AEMBIT_HTTP_PORT_DISABLED}"
    fi

    if ! sed --in-place "s#{{ OPTIONAL_AEMBIT_HTTP_PORT_DISABLED }}#${optional_aembit_http_port_disabled}#" "${SYSTEMD_UNIT_FILES_FOLDER}"/"${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to update optional HTTP Port Disabled setting in the systemd service file."
        exit 1
    fi

    if ! systemctl enable "${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to enable the Agent Controller systemd service."
        exit 1
    fi
}

start_systemd_service() {
    if ! systemctl start "${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to start the Agent Controller systemd service."
        exit 1
    fi
}

upgrade_systemd_service() {
    if ! systemctl daemon-reload; then
        log_err "Unable to reload the Agent Controller systemd service."
        exit 1
    fi

    if ! systemctl restart "${AEMBIT_AGENT_CONTROLLER_SYSTEMD_UNIT}"; then
        log_err "Unable to restart the updated Agent Controller systemd service."
        exit 1
    fi

    if ! systemctl restart systemd-journald; then
        log_err "Unable to restart journald with the updated log configuration."
        exit 1
    fi
}

remove_all_previous_installations() {
    log_info "Deleting old Agent Controller versions."
    for dir in ${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR}/${AEMBIT_AGENT_CONTROLLER_VERSION_PATTERN}; do
        case "${dir}" in
            # Don't delete the upgraded Agent Controller version.
            *"${AEMBIT_AGENT_CONTROLLER_VERSION}"*)
                # Do nothing
                ;;
            *)
                rm -rf "${dir}"
                ;;
        esac
    done
}

display_selinux_setup() {
    if ! sed --in-place "s#AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN#${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN}#" "${SELINUX_GENERATE_POLICY_SCRIPT}"; then
        log_err "Unable to update Agent Controller binary path in the SELinux policy generation script."
        exit 1
    fi
    
    if ! is_platform "rhel"; then
        return 0
    fi

    if ! command -v selinuxenabled &> /dev/null; then
        # "standard" selinuxenabled command not found
        return 0
    fi

    if ! selinuxenabled; then
        return 0
    fi

    log_info "SELinux detected. Utilities for running Aembit Agent Controller under SELinux are located in ./selinux"
    log_info "See the Aembit technical documentation at https://docs.aembit.io/deployments/virtual-machine/selinux to get started"
}

run_installer() {
    check_running_as_root || exit 1
    parse_options "${@}"

    check_for_required_env_var AEMBIT_TENANT_ID || exit 1
    check_registration_method || exit 1
    check_tls_methods_exclusivity || exit 1
    set_default_stack_domain

    check_required_dependencies || exit 1
    handle_existing_agent_controller || exit 1
    create_user_and_group || exit 1
    set_up_logs || exit 1
    copy_static_components_to_host || exit 1
    copy_tls_cert
    install_systemd_service || exit 1
    display_selinux_setup || exit 1
    check_user_file_access "${AEMBIT_AGENT_CONTROLLER_USER_NAME}" "${AEMBIT_AGENT_CONTROLLER_INSTALL_DIR_BIN}/${AEMBIT_AGENT_CONTROLLER_BIN}"  || exit 1 

    if is_upgrade; then
        upgrade_systemd_service || exit 1
        remove_all_previous_installations || exit 1
    else
        start_systemd_service || exit 1
    fi

}

main() {
    set -o pipefail
    log_info "Installing Aembit Agent Controller."
    run_installer "${@}" 2>&1
}

main "${@}" || exit 1
