#!/bin/bash

# =============================================================================
# launch_script Scope
# =============================================================================
# 1. Check the requirements needed to run NodeZero and generated warnings or errors if those checks fail. 
# 2. Clean up previous NodeZero artifacts that may be left behind from previous runs.
# 3. Run independently as a check script on a NodeZero host system
# =============================================================================

# Check if interactive shell is available
USE_INTERACTIVE_SHELL=false
if [[ $- == *i* ]]; then
    USE_INTERACTIVE_SHELL=true
fi





# Formatting Variables
if [[ $USE_INTERACTIVE_SHELL == true ]]; then
    BOLD=$(tput bold)
    NORMAL=$(tput sgr0)
    RED=$(tput setaf 1)
    GREEN=$(tput setaf 2)
    YELLOW=$(tput setaf 3)
    MAGENTA=$(tput setaf 5)
    CYAN=$(tput setaf 6)
    FANCY=$(echo -e "cuu1\nel" | tput -S)
else
    BOLD=""
    NORMAL=""
    RED=""
    GREEN=""
    YELLOW=""
    MAGENTA=""
    CYAN=""
    FANCY=""
fi

# Create utility functions
function HeaderMsg {
    echo -e "\n${BOLD}[${CYAN}#${NORMAL}${BOLD}] ${CYAN}$@${NORMAL}"
}


function PassMsg {
    echo -e "${BOLD}[${GREEN}+${NORMAL}${BOLD}] ${NORMAL}${GREEN}PASSED: $@${NORMAL}"
}


function GenMsg {
    echo -e "${BOLD}[${GREEN}+${NORMAL}${BOLD}]${NORMAL}${GREEN} $@${NORMAL}"
}


function FailMsg {
    echo -e "${BOLD}[${RED}!${NORMAL}${BOLD}] ${NORMAL}${RED}FAILED: $@${NORMAL}"
}


function WarnMsg {
    echo -e "${BOLD}[${YELLOW}!${NORMAL}${BOLD}] ${NORMAL}${YELLOW}WARNING: $@${NORMAL}"
}


function AskMsg {
    read -p "${BOLD}[${MAGENTA}-${NORMAL}${BOLD}] ${NORMAL}${MAGENTA}CONFIRM: $@ (y|n) ${NORMAL}" -n 1 -r </dev/tty
}


function ExitMsg {
    echo -e "\n${BOLD}[${CYAN}#${NORMAL}${BOLD}] ${CYAN}$@${NORMAL}"
}

# Global Variables
UNAMES=$(uname -s)
UNAMER=$(uname -r)

# Detect container runtime (prefer docker, fallback to podman)
if command -v docker &> /dev/null && ! docker --version 2>/dev/null | grep -qi podman; then
    CONTAINER_RUNTIME=docker
elif command -v podman &> /dev/null; then
    CONTAINER_RUNTIME=podman
    WarnMsg "Docker not found. Using Podman as container runtime."
else
    FailMsg "Neither Docker nor Podman found. Please install Docker or Podman and retry."
    exit 1
fi

# Detect SELinux status
SELINUX_ENABLED=false
if command -v getenforce &> /dev/null && [[ $(getenforce 2>/dev/null) != "Disabled" ]]; then
    SELINUX_ENABLED=true
fi

# Set volume suffix for SELinux bind mounts
if [[ $SELINUX_ENABLED == true ]]; then
    VOLUME_SUFFIX=":z"
else
    VOLUME_SUFFIX=""
fi

# Update CONTAINER_CMD and CONTAINER_RUN with detected runtime
if [[ $CONTAINER_RUNTIME == "podman" ]] || [[ $SUDO_REQ == YES ]]; then
    CONTAINER_CMD="sudo $CONTAINER_RUNTIME"
else
    CONTAINER_CMD=$CONTAINER_RUNTIME
fi

PROXY_CONFIGURED=false

# Check for proxy settings
# Docker: ~/.docker/config.json, Podman: ~/.config/containers/containers.conf
docker_proxy_config="$HOME/.docker/config.json"
if [[ $CONTAINER_RUNTIME == "docker" ]] && [ -f $docker_proxy_config ]; then
    if [ $(cat $docker_proxy_config | grep -io '}' | wc -l) -ge 2  ]; then

        # Check if proxy is set in default config
        if [ $(grep "httpsProxy" $docker_proxy_config | wc -l) -eq 1 ]; then

            # Find proxy and extract value
            proxy=`grep -o 'httpsProxy"\: "[^"]*' $docker_proxy_config | sed "s/.*\"//g"`
            if [ ! -z "$proxy" ]; then
                gateway_proxy_config='"proxies": {
                    "default": {
                        "httpProxy": "'${proxy}'",
                        "httpsProxy": "'${proxy}'",
                        "noProxy": "localhost,127.0.0.1,::1,172.16.0.0/16,10.0.0.0/8,192.168.0.0/16"
                    }
                }'
            fi
            # Proxy is empty
        fi
        # did not find "httpsProxy" in file
    fi
    # File does not have json content, or is empty
fi

if [[ "$CONTAINER_RUNTIME" == "podman" ]]; then
    container_proxy_config="$HOME/.config/containers/containers.conf"
    if [ -f "$container_proxy_config" ]; then
        # Podman containers.conf uses TOML with env = ["https_proxy=..."]
        if grep -qi 'https_proxy' "$container_proxy_config" 2>/dev/null; then
            proxy=$(grep -i 'https_proxy' "$container_proxy_config" | grep -o 'http[s]*://[^"]*' | head -1)
        fi
    fi
fi

if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
    unset DOCKER_CONFIG_PATH
    DOCKER_CONFIG_PATH=$(mktemp -d -t docker-XXXX)
fi



REGISTRY_URL="gateway.horizon3ai.au"
REPOSITORY="docker/library"
IMAGE="$REGISTRY_URL/$REPOSITORY/hello-world:latest"
# If consolidated endpoint is NOT configured, use default time API for now
TIME_API="https://api.horizon3ai.com/v1/time"
# Docker-specific config: write HttpHeaders and auths to temp config.json
if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
    # Proxy not found or empty
    if [ -z "${gateway_proxy_config}" ]; then
        cat > $DOCKER_CONFIG_PATH/config.json << EOF
{
    "HttpHeaders": {
        "X-Meta-Docker": "True"
    },
    "auths": {
        "gateway.horizon3ai.au": {}
    }
}
EOF

    # Proxy was found
    else
        cat > $DOCKER_CONFIG_PATH/config.json << EOF
{
    "HttpHeaders": {
        "X-Meta-Docker": "True"
    },
    "auths": {
        "gateway.horizon3ai.au": {}
    },
    $gateway_proxy_config
}
EOF
    fi
fi



# Set up CONTAINER_RUN for running containers
# Docker: --config is a global flag (before subcommand) for HttpHeaders and auths
# Podman: does not use --config; uses registry. subdomain to route through public ECR
if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
    CONTAINER_RUN="$CONTAINER_CMD --config $DOCKER_CONFIG_PATH"
else
    CONTAINER_RUN="$CONTAINER_CMD"
    # Podman uses the registry. subdomain so the /v2/ ping and pulls
    # route to public ECR via the gateway's ecr-public-registries route
    REGISTRY_URL="registry.$REGISTRY_URL"
    IMAGE="$REGISTRY_URL/$REPOSITORY/hello-world:latest"
fi


# Docker proxy config checks and evaluation
# Sets: PROXY_CONFIGURED
function DockerProxyChk {
    local docker_config=false
    local docker_daemon=false
    local docker_service_proxy=false

    # Check ~/.docker/config.json (user's actual config, not the temp one we create)
    if [ -f ${DOCKER_CONFIG_PATH}/config.json ]; then
        docker_config_path_proxies=`sudo cat ${DOCKER_CONFIG_PATH}/config.json | grep -io proxy | wc -l`
        GenMsg "Found ${docker_config_path_proxies} of 3 proxy settings in ${DOCKER_CONFIG_PATH}/config.json"
        if [ $docker_config_path_proxies -ge 3 ]; then
            docker_config=true
        fi
    fi

    # Check /etc/docker/daemon.json
    if [ -f /etc/docker/daemon.json ]; then
        docker_daemon_proxies=`sudo cat /etc/docker/daemon.json | grep -io proxy | wc -l`
        GenMsg "Found ${docker_daemon_proxies} of 3 proxy settings in /etc/docker/daemon.json"
        if [ $docker_daemon_proxies -ge 3 ]; then
            docker_daemon=true
        fi
    fi

    # Check /etc/systemd/system/docker.service.d/http-proxy.conf
    if sudo [ -f /etc/systemd/system/docker.service.d/http-proxy.conf ]; then
        docker_service_proxies=`sudo cat /etc/systemd/system/docker.service.d/http-proxy.conf | grep -io proxy | wc -l`
        GenMsg "Found ${docker_service_proxies} of 6 proxy settings in /etc/systemd/system/docker.service.d/http-proxy.conf"
        if [ $docker_service_proxies -ge 6 ]; then
            docker_service_proxy=true
        fi
    fi

    # Evaluate Docker proxy state
    if [ "$docker_config" = true ] && [ "$docker_daemon" = true ] && [ "$docker_service_proxy" = true ] && [ "$ENV_PROXIES" = true ] && [ "$ENV_VARS" = true ]; then
        PROXY_CONFIGURED=true
        PassMsg "Proxy is detected"
    elif [ "$docker_config" = false ] && [ "$docker_daemon" = false ] && [ "$docker_service_proxy" = false ] && [ "$ENV_PROXIES" = false ] && [ "$ENV_VARS" = false ]; then
        PROXY_CONFIGURED=false
        PassMsg "Proxy is NOT configured"
    else
        # Dont exit, there could be multiple proxies or commented out etc...
        FailMsg "Proxy is not configured correctly, Please refer to https://docs.horizon3.ai/proxy-setup for guidance on setting a proxy"
    fi
}

# Podman proxy config checks and evaluation
# Sets: PROXY_CONFIGURED
function PodmanProxyChk {
    local podman_config_found=false

    # Podman reads proxy settings from ~/.config/containers/containers.conf
    podman_config="$HOME/.config/containers/containers.conf"
    if [ -f "$podman_config" ]; then
        podman_proxy_count=$(sudo cat ${podman_config} | grep -io proxy | wc -l)
        podman_proxy_count=${podman_proxy_count:-0}
        GenMsg "Found ${podman_proxy_count} of 3 proxy settings in $podman_config"
        if [ "$podman_proxy_count" -ge 3 ]; then
            podman_config_found=true
        fi
    fi

    # Evaluate Podman proxy state
    if [ "$podman_config_found" = true ] && [ "$ENV_PROXIES" = true ] && [ "$ENV_VARS" = true ]; then
        PROXY_CONFIGURED=true
        PassMsg "Proxy is detected"
    elif [ "$podman_config_found" = false ] && [ "$ENV_PROXIES" = false ] && [ "$ENV_VARS" = false ]; then
        PROXY_CONFIGURED=false
        PassMsg "Proxy is NOT configured"
    else
        # Dont exit, there could be multiple proxies or commented out etc...
        FailMsg "Proxy is not configured correctly, Please refer to https://docs.horizon3.ai/proxy-setup for guidance on setting a proxy"
    fi
}

# Check if a proxy is configured and setup
function ProxyChk {
    HeaderMsg "Checking Proxy settings and configurations:"

    ENV_VARS=false
    ENV_PROXIES=false
    PROXY_CONFIGURED=false

    # Check /etc/environment
    if [ -f /etc/environment ]; then
        env_proxies=`sudo cat /etc/environment | grep -ic proxy`
        GenMsg "Found ${env_proxies} of 6 proxy settings in /etc/environment"
        if [ $env_proxies -ge 6 ]; then
            ENV_PROXIES=true
        fi
    fi

    # Check if shell environment has http_proxy set
    env_shell_proxies=`env | grep -ic 'http_proxy\|https_proxy\|no_proxy'`
    GenMsg "Found ${env_shell_proxies} of 6 proxy settings in env"
    if [ $env_shell_proxies -ge 6 ]; then
        ENV_VARS=true
    fi

    # Runtime-specific proxy config checks and evaluation
    if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
        DockerProxyChk
    else
        PodmanProxyChk
    fi
}


# Validate supported OS is being used and set OS specific variables
function OsChk {

    HeaderMsg "Checking Operating System:"

    case $UNAMES in
        Linux*)
            OS="Linux"
            PassMsg "$OS is a supported Operating System."

            ArchChk

            HeaderMsg "Gathering environmental variables to conduct further checks:"
            if [[ $UNAMER =~ microsoft ]]; then
                FREE_SPACE_KB=$(df -Pk /init | awk 'NR==2 { print $4 }')
            else
                if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
                    CONTAINER_STORAGE_PATH=$($CONTAINER_CMD info 2>/dev/null | grep -i "Docker Root Dir" | sed 's/Docker Root Dir:\ //')
                else
                    CONTAINER_STORAGE_PATH=$($CONTAINER_CMD info --format '{{.Store.GraphRoot}}' 2>/dev/null)
                fi
                FREE_SPACE_KB=$(df -Pk ${CONTAINER_STORAGE_PATH:-.} | awk 'NR==2 { print $4 }')
            fi
            SPACE_DOC="https://docs.docker.com/config/pruning/"
            FS_DOC="https://docs.docker.com/docker-for-windows/#file-sharing"
            MEM_B=$(free -b | awk 'NR==2 { print $2 }')
            CPU=$(grep -c ^processor /proc/cpuinfo)
            PassMsg "All environmental variables set and proceeding with next checks."

            TimeChk
            ;;
        Darwin*)
            OS="macOS"
            PassMsg "$OS is a supported Operating System."

            ArchChk

            HeaderMsg "Gathering environmental variables to conduct further checks:"
            if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
                DOCKER_RAW="$HOME/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw"
                if [[ ! -f $DOCKER_RAW ]]; then
                    WarnMsg "A Docker configuration file is not in it's default location, an attempt to find it will be made that may ask for access to folders. If disallowed to search, the disk size validation check will not complete and report as a failed check."
                    DOCKER_RAW=$(find $HOME/Library/Containers -name Docker.raw 2>/dev/null | grep .)
                    EXIT=$?
                    if [[ $EXIT == 1 ]]; then
                        FailMsg "Unable to locate the Docker.raw file to validate enough HDD space is available. Please ensure Docker is installed properly and retry. See https://docs.docker.com/docker-for-mac/install/ for more information on how to install Docker Desktop for macOS."
                        exit 1
                    fi
                fi
                USED_SPACE_KB=$(ls -ksn $DOCKER_RAW | awk '{ print $1 }')
                MAX_SPACE_KB=$(( $(ls -ksn $DOCKER_RAW | awk '{ print $6 }') / 1024 ))
                FREE_SPACE_KB=$(( $MAX_SPACE_KB - $USED_SPACE_KB ))
                SPACE_DOC="https://docs.docker.com/docker-for-mac/space/"
                FS_DOC="https://docs.docker.com/docker-for-mac/#file-sharing"
            else
                # Podman on macOS uses a different VM approach; use df for free space
                FREE_SPACE_KB=$(df -Pk . | awk 'NR==2 { print $4 }')
                SPACE_DOC="https://docs.podman.io"
                FS_DOC="https://docs.podman.io"
            fi

            MEM_B=$(sysctl -a | grep hw.memsize_usable | awk 'NR==1{ print $2 }')
            if [[ -z "$MEM_B" ]]; then
                MEM_B=$(sysctl -a | grep hw.mem | awk 'NR==1{ print $2 }')
            fi
            CPU=$(sysctl -a | grep hw.logicalcpu_max | awk '{ print $2 }')
            if [[ -z "$MEM_B" ]]; then
                FailMsg "Unable to determine system properties. Please ensure sysctl can be found on the system PATH."
                exit 1
            fi
            PassMsg "All environmental variables set and proceeding with next checks."

            TimeChk
            ;;
        *)
            FailMsg "$UNAMES is not a currently supported Operating System. Please provide feedback to add this Operating System."
            exit 1
            ;;
    esac
}


# Check time against UTC for max of 5 minutes of drift
# Test time drift by disabling ntp and adjusting time using:
# timedatectl set-ntp off
# date -s "now - 12 minutes"
function TimeChk {

    HeaderMsg "Checking host time against current UTC time:"

    TIME=$(curl -k -s -m 3 $TIME_API)
    EXIT=$?
    if [[ $EXIT != 0 ]]; then
        sleep 30
        TIME=$(curl -k -s -m 3 $TIME_API)
    	EXIT=$?
    	if [[ $EXIT != 0 ]]; then
            WarnMsg "Unable to determine current UTC time to validate time is within a 5 minute variance. The URL $TIME_API was unreachable."
            return 0
        fi
	fi

    VALID_TIME=$(echo "$TIME" | grep EpochTime)
    EXIT=$?
    if [[ $EXIT != 0 ]]; then
        WarnMsg "Unable to determine current UTC time to validate time is within a 5 minute variance. The URL $TIME_API did not return a valid timestamp."
        return 0
    fi

    declare -i UTC=$(echo "$TIME" | sed -n 's/.*"EpochTime"\s*:\s*"\([0-9]\{10\}\).*/\1/p')
    declare -i LOCAL=$(date +%s)

    ABSDIFF=$(( UTC > LOCAL ? UTC - LOCAL : LOCAL - UTC ))
    if [[ $ABSDIFF -le 300 ]]; then
        PassMsg "System time is within 5 minutes of UTC time."
    else
        FailMsg "The system time is off by more than 5 minutes from UTC time. Please set or resync your system time before running again. For additional information, see: (https://docs.horizon3.ai/t/time-oos)"
        exit 1
    fi
}


# Check if you can run the container runtime
function ContainerRuntimeChk {

    HeaderMsg "Checking ${CONTAINER_RUNTIME^} functionality by running the hello-world test container:"

    CleanUp

    if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
        declare -i DOCKER_VERSION=$($CONTAINER_RUNTIME -v | awk '{ print $3 }' | awk -F '.' '{ print $1 }')
        if [[ $DOCKER_VERSION -ge 20 ]]; then
            PassMsg "Docker version installed meets the minimum required version 20.10."
        else
            FailMsg "Docker major version $DOCKER_VERSION is unsupported, please upgrade Docker to the minimum required version 20.10."
            exit 1
        fi
    else
        declare -i PODMAN_VERSION=$($CONTAINER_RUNTIME -v | awk '{ print $3 }' | awk -F '.' '{ print $1 }')
        if [[ $PODMAN_VERSION -ge 4 ]]; then
            PassMsg "Podman version installed meets the minimum required version 4.0."
        else
            FailMsg "Podman major version $PODMAN_VERSION is unsupported, please upgrade Podman to the minimum required version 4.0."
            exit 1
        fi
    fi

    local retry_count=0
    local max_retries=5
    local EXIT=0

    $CONTAINER_RUN run --rm --name h3-test $IMAGE &> /dev/null
    EXIT=$?

    while [[ $EXIT -eq 125 && $retry_count -lt $max_retries ]]; do
        FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Retrying check (attempt $retry_count)..."

        retry_count=$((retry_count + 1))
        sleep 2  # Wait for 2 second before retrying

        $CONTAINER_RUN run --rm --name h3-test $IMAGE &> /dev/null
        EXIT=$?
    done

    case $EXIT in
        0*)
            PassMsg "${CONTAINER_RUNTIME^} is installed and functioning properly."
            PermChk
            ;;
        125*)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Verify the ${CONTAINER_RUNTIME} daemon/service is started and retry."
            return 1
            ;;
        126*)
            
            SudoChk
            
            ;;
        127*)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Verify that ${CONTAINER_RUNTIME} is installed and retry."
            return 1
            ;;
        *)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Please ensure ${CONTAINER_RUNTIME} is installed and this user has permission to execute."
            return 1
            ;;
    esac

    CleanUp
}


# Retry container runtime check with "docker hub sourced hello-world" image if the initial check fails
function RetryContainerRuntimeChk {

    HeaderMsg "Retrying ${CONTAINER_RUNTIME^} functionality check with Docker Hub sourced hello-world image:"

    # Deprecation message
    WarnMsg "Deprecation Notice: Docker Hub (docker.io) sourced images will no longer be available after the May 2025 release."
    WarnMsg "Please update your network allowlist configuration per https://docs.horizon3.ai/quickstart/network_requirements/ before May 2025."

    REGISTRY_URL="docker.io"
    REPOSITORY="library"
    IMAGE="$REGISTRY_URL/$REPOSITORY/hello-world:latest"

    $CONTAINER_RUN run --rm --name h3-test $IMAGE &> /dev/null
    EXIT=$?

    case $EXIT in
        0*)
            PassMsg "${CONTAINER_RUNTIME^} is installed and functioning properly with the hello-world image."
            PermChk
            ;;
        125*)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^} with the hello-world image. Verify the ${CONTAINER_RUNTIME} daemon/service is started and retry."
            exit 1
            ;;
        126*)
            
            SudoChk
            
            ;;
        127*)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^} with the hello-world image. Verify that ${CONTAINER_RUNTIME} is installed and retry."
            exit 1
            ;;
        *)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^} with the hello-world image. Please ensure ${CONTAINER_RUNTIME} is installed and this user has permission to execute."
            exit 1
            ;;
    esac

    CleanUp

}


function ContainerProxyCheck {

    if [ "$PROXY_CONFIGURED" = true ]; then
        HeaderMsg "Checking ${CONTAINER_RUNTIME^} proxy settings:"

        local retry_count=0
        local max_retries=5
        local EXIT=0

        container_proxies=`$CONTAINER_RUN run --rm $REGISTRY_URL/$REPOSITORY/alpine sh -c 'env | grep -ic proxy'`
        EXIT=$?

        while [[ $EXIT -eq 125 && $retry_count -lt $max_retries ]]; do
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}, retrying check (attempt $retry_count)..."

            retry_count=$((retry_count + 1))
            sleep 2  # Wait for 2 second before retrying

            container_proxies=`$CONTAINER_RUN run --rm $REGISTRY_URL/$REPOSITORY/alpine sh -c 'env | grep -ic proxy'`
            EXIT=$?
        done

        if [ $retry_count -eq $max_retries ]; then
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Verify the ${CONTAINER_RUNTIME} daemon/service is started and retry."
            exit 1
        fi

        GenMsg "Found ${container_proxies} of 6 proxy settings in env"
        AlpineCleanUp
        if [ $container_proxies -eq 6 ]; then
            PassMsg "Container has proxy configured"
        else
            FailMsg "Proxy environment variables not found in container"
            exit 1
        fi
    fi
}


# Check permissions for container runtime to mount the read-only Op Config
function PermChk {

    # Use $HOME instead of $PWD for the mount test — the runner service may
    # launch from / which is not writable or mountable in most configurations
    PERM_CHK_DIR="${HOME:-.}"
    HeaderMsg "Checking ${CONTAINER_RUNTIME^} permissions to volume mount files from $PERM_CHK_DIR directory:"

    mkdir -p $PERM_CHK_DIR/h3.txt &> /dev/null
    $CONTAINER_RUN run --rm --name h3-test -v $PERM_CHK_DIR/h3.txt:/h3.txt${VOLUME_SUFFIX} $IMAGE &> /dev/null
    EXIT=$?
    rmdir $PERM_CHK_DIR/h3.txt &> /dev/null

    case $EXIT in
        0*)
            PassMsg "${CONTAINER_RUNTIME^} permissions are correct for the $PERM_CHK_DIR directory location."
            CleanUp
            ;;
        125*)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. The path $PERM_CHK_DIR is not shared and is not known to ${CONTAINER_RUNTIME}. Retry from a directory path that allows ${CONTAINER_RUNTIME} to mount files from $PERM_CHK_DIR. See $FS_DOC for more information."
            exit 1
            ;;
        *)
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Please ensure ${CONTAINER_RUNTIME} is installed and this user has permission to execute."
            exit 1
            ;;
    esac

    CleanUp
}


# Check if sudo is required to run container runtime commands
function SudoChk {

    WarnMsg "Unable to validate ${CONTAINER_RUNTIME^} due to insufficient privileges."
    AskMsg "Would you like to retry using sudo privileges?"

    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then

        HeaderMsg "Attempting to validate ${CONTAINER_RUNTIME^} using sudo privileges:"

        # Check that sudo is used
        if echo "$CONTAINER_RUN" | grep -q 'sudo'; then
            SUDO_CONTAINER_RUN=$CONTAINER_RUN
        else
            SUDO_CONTAINER_RUN="sudo $CONTAINER_RUN"
        fi

        local retry_count=0
        local max_retries=5
        local EXIT=0

        $SUDO_CONTAINER_RUN run --rm --name h3-test $IMAGE &> /dev/null
        EXIT=$?

        while [[ $EXIT -eq 125 && $retry_count -lt $max_retries ]]; do
            FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Retrying check (attempt $retry_count)..."

            retry_count=$((retry_count + 1))
            sleep 2  # Wait for 2 second before retrying

            $SUDO_CONTAINER_RUN run --rm --name h3-test $IMAGE &> /dev/null
            EXIT=$?
        done

        case $EXIT in
            0*)
                WarnMsg "Check completed successfully, sudo will be used to run all ${CONTAINER_RUNTIME^} commands with this user."
                SUDO_REQ="YES"
                CONTAINER_CMD="sudo $CONTAINER_RUNTIME"
                if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
                    CONTAINER_RUN="$CONTAINER_CMD --config $DOCKER_CONFIG_PATH"
                else
                    CONTAINER_RUN="$CONTAINER_CMD"
                fi
                PermChk
                ;;
            1*)
                FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Verify this account is in the sudoers file and retry."
                exit 1
                ;;
            126*)
                FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Verify this account has permissions to run ${CONTAINER_RUNTIME} and retry."
                exit 1
                ;;
            *)
                FailMsg "Failed to validate ${CONTAINER_RUNTIME^}. Please ensure ${CONTAINER_RUNTIME} is installed and this user has permission to execute."
                exit 1
                ;;
        esac

        CleanUp
    else
        CleanUp
        ExitMsg "Exiting without validating ${CONTAINER_RUNTIME^}, please allow this user ${CONTAINER_RUNTIME} privileges and retry."
        exit 1
    fi
}


# Check if disk space has room; $FREE_SPACE_KB is set in OsChk().
function DiskChk {

    HeaderMsg "Checking HDD space requirements (minimum 30GB Recommended, 20GB Required):"

    # IMG_SIZE_KB is to account for NodeZero image size if provided.
    if [[ $IMG_SIZE_KB -gt 0 ]]; then
        FREE_SPACE_KB=$(( $FREE_SPACE_KB + $IMG_SIZE_KB ))
    fi

    FREE_SPACE_GB="$(( $FREE_SPACE_KB / 1024 / 1024 ))GB"
    if [[ $FREE_SPACE_KB -ge 31457280 ]]; then
        PassMsg "There is enough space for the NodeZero container: $FREE_SPACE_GB"
    elif [[ $FREE_SPACE_KB -le 20971520 ]]; then
        FailMsg "$FREE_SPACE_GB is not enough space to support NodeZero. Remove unused containers and volumes to reclaim space and retry. See $SPACE_DOC for more information on how to remove unused images or containers to reclaim space."  # Only exit with checkscript, for launch_script allow the script to continue
        exit 1
        
    else
        WarnMsg "$FREE_SPACE_GB is less than the recommended 30GB free space on this disk, please ensure to prune old images before running Node Zero again."
    fi
}


# Check if enough memory; $MEM_B variable should be in bytes
function MemChk {

    HeaderMsg "Checking 8GB RAM requirement:"

    MEM_GB="$(( $MEM_B / 1024 / 1024 / 1024 ))GB"
    # Leave a little buffer for kernel reservations
    if [[ $MEM_B -ge 8053063680 ]]; then
        PassMsg "This system meets the recommended minimum RAM to support NodeZero."
    else
        WarnMsg "It is recommended to have a minimum of 8GB RAM to run NodeZero. This system has $MEM_GB RAM and you may experience sluggish NodeZero performance."
    fi
}


# Check if enough compute
function CpuChk {

    HeaderMsg "Checking compute resource requirements:"

    if [[ $CPU -ge 2 ]]; then
        PassMsg "This system has $CPU CPUs which meets the minimum logical CPU requirements to run NodeZero."
    else
        WarnMsg "It is recommended to have a minimum of 2 logical CPUs to run NodeZero. This system has $CPU CPUs and you may experience sluggish NodeZero performance."
    fi
}


# Check if CPU architecture is supported (x86_64/amd64 only)
function ArchChk {

    HeaderMsg "Checking CPU architecture compatibility:"

    ARCH=$(uname -m)

    case $ARCH in
        x86_64|amd64)
            PassMsg "CPU architecture '$ARCH' is supported by NodeZero."
            ;;
        arm64|aarch64|armv*)
            FailMsg "CPU architecture '$ARCH' is not supported. NodeZero requires an x86_64 (amd64) processor. ARM-based systems (including Apple Silicon Macs and ARM servers) are not supported."
            exit 1
            ;;
        *)
            FailMsg "Unknown CPU architecture '$ARCH'. NodeZero requires an x86_64 (amd64) processor."
            exit 1
            ;;
    esac
}


function EdrDefinitions {
    # Get list of common EDR processes to check for
    # CrowdStrike Falcon (falcon)
    cs_falcon="csfalconservice|csagent|falcon"

    # Microsoft Defender for Endpoint (mdatp)
    ms_defender="msmpeng|mssense|nissrv|sensendr|mdatp"

    # SentinelOne Singularity (sentinel)
    sentinel="sentinelone|sentineld|sentinelctl"

    # Palo Alto Networks Cortex XDR (pmd|dypd|traps)
    pa_cortex="pmd|dypd|traps|cortex|cyveraservice|cyserver"

    # Sophos Intercept X (sophos)
    sophos="savservice|sophoshealth|sophossps|sophosfilescanner|sophosclean|sophososquery"

    # Elastic Security (elastic)
    elastic="elastic-endpoint|elastic-agent"

    # VMware Carbon Black (cbagent)
    vm_cb="cbdaemon|cbagent|cbdefense|repmgr|rtvscand"

    # Symantec/Broadcom
    sb="ccsvchst|smc|symcorpui"

    # Cisco Secure Endpoint (amp)
    cisco_se="ampcli|ampscansvc|ampdaemon"

    # Cybereason (cybereason)
    cr="cybereason|minionhost|crsensor"

    # Cylance/ArcticWolf (cylance)
    aw="arcticwolfagent|arcticwolfdesktop|cylancesvc|wazuh"

    # McAfee/Trellix/FireEye
    mcafee="mfetpd|mfetp|mfemactl|isectpd|mvision|mvedr|mvedrcontrol|xagt"

    # Trend Micro/Apex One
    tm="tmntsrv|tmlisten|ds_agent"

    # Bitdefender
    bitdefender="bdagent|vsserv|bdservice|bdsec|bitdefender-security-tools"

    # Fortinet
    fortinet="fortiedr|fortiesnac"

    # Malwarebytes
    malwarebytes="mbamservice|mbam"

    # Tanium
    tanium="taniumclient"

    # Rapid7
    rapid7="ir_agent"

    echo "$cs_falcon|$ms_defender|$sentinel|$pa_cortex|$sophos|$elastic|$vm_cb|$cisco_se|$cr|$aw|$sb|$mcafee|$tm|$bitdefender|$fortinet|$malwarebytes|$tanium|$rapid7"

}

# Checks if an EDR is running on the NodeZero host that may interfere with NodeZero operations.
function EdrChk {

    local EDR_DEFINITIONS=$(EdrDefinitions)

    # EDR_PROCESSES=$(ps aux | grep -i "$EDR_DEFINITIONS" | awk '{print $2, $11}' | grep -v 'grep')
    EDR_PROCESSES=$(pgrep -aif "$EDR_DEFINITIONS" || true)

    if command -v systemctl &> /dev/null; then
        EDR_SERVICES=$(systemctl list-units | grep -i "$EDR_DEFINITIONS")
    fi


    if [[ -n "$EDR_PROCESSES" ]] || [[ -n "$EDR_SERVICES" ]]; then
        WarnMsg "EDR processes or services detected!!!"
        WarnMsg "Please disable the EDR on this system that may interfere with NodeZero operations."

        if [[ -n "$EDR_PROCESSES" ]]; then
            WarnMsg "Detected EDR Processes:"
            echo "$EDR_PROCESSES"
        fi
        if [[ -n "$EDR_SERVICES" ]]; then
            WarnMsg "Detected EDR Services:"
            echo "$EDR_SERVICES"
        fi
        sleep 5 # Sleep to allow user to read the detected EDR processes and services before the script continues
    else
        PassMsg "No common EDR processes or services detected on this system."
    fi
}


# Establish a TLS connection and emit the server certificate text.
# Args: $1 = SNI/servername, $2 = host to connect to, $3 = port
# Echoes the openssl s_client output (empty output indicates a connection failure)
# and returns the openssl/timeout exit code.
function TlsConnect {
    local sni="$1" connect_host="$2" port="$3"

    # Use timeout on Linux, gtimeout on macOS (if available), or no timeout as fallback
    if [[ "$UNAMES" == "Darwin" ]]; then
        if command -v gtimeout &>/dev/null; then
            echo | gtimeout 10 openssl s_client -servername "$sni" -connect "$connect_host:$port" 2>/dev/null
        else
            echo | openssl s_client -servername "$sni" -connect "$connect_host:$port" 2>/dev/null
        fi
    else
        echo | timeout 10 openssl s_client -servername "$sni" -connect "$connect_host:$port" 2>/dev/null
    fi
}


# Validate a certificate's expiration from openssl output and print PASS/WARN/FAIL.
# The certificate hostname/SAN is intentionally NOT verified so this works for
# wildcard/dynamic subdomains; we only care that the cert is reachable and unexpired
# (an expired/garbage cert indicates a firewall/proxy intercepting HTTPS traffic).
# Args: $1 = openssl cert output, $2 = display name. Updates NETWORK_WARNINGS.
function CheckCertExpiry {
    local cert_output="$1" display="$2"
    local not_after end_epoch now_epoch

    not_after=$(echo "$cert_output" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//')
    if [[ -z "$not_after" ]]; then
        echo -e "${RED}FAIL${NORMAL}"
        WarnMsg "Could not parse SSL certificate for $display"
        NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
        return
    fi

    # Convert certificate expiration to epoch (handle both Linux and macOS date formats)
    if [[ "$UNAMES" == "Darwin" ]]; then
        end_epoch=$(date -j -f "%b %d %H:%M:%S %Y %Z" "$not_after" +%s 2>/dev/null)
    else
        end_epoch=$(date -d "$not_after" +%s 2>/dev/null)
    fi
    now_epoch=$(date +%s)

    if [[ -z "$end_epoch" ]]; then
        echo -e "${YELLOW}WARN${NORMAL}"
        WarnMsg "Could not determine certificate expiration date for $display"
        NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
    elif [[ $now_epoch -gt $end_epoch ]]; then
        echo -e "${RED}FAIL${NORMAL}"
        WarnMsg "SSL certificate for $display has EXPIRED (expired: $not_after)"
        WarnMsg "  This may indicate a firewall/proxy intercepting HTTPS traffic with an expired certificate"
        WarnMsg "  Please ensure $display is allowlisted in your firewall. See $NETWORK_DOC"
        NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
    else
        echo -e "${GREEN}PASS${NORMAL}"
    fi
}


# Check network connectivity and SSL certificate validity for required hosts
# Reference: https://docs.horizon3.ai/quickstart/network_requirements/#your-portal-region
function NetworkChk {

    HeaderMsg "Checking network connectivity and SSL certificates for required hosts:"

    NETWORK_WARNINGS=0
    NETWORK_DOC="https://docs.horizon3.ai/quickstart/network_requirements/"

    # Wildcard hosts (the "*." patterns from network requirements). NodeZero reaches
    # dynamically-named subdomains under these, so they are checked via SNI probe below.
    # Populated per-region; empty by default (e.g. gateway-only regions need none).
    WILDCARD_HOSTS_443=()
    WILDCARD_HOSTS_80=()

    # Determine which hosts to check based on the gateway/registry URL
    
    GATEWAY_HOST="gateway.horizon3ai.au"
    USING_GATEWAY="false"
    

    # Define hosts based on region (URLs for SSL certificate checks)
    if [[ "$GATEWAY_HOST" == *"horizon3ai.eu"* && "$USING_GATEWAY" == "false" ]]; then
        # EU Region hosts - https://docs.horizon3.ai/quickstart/network_requirements/#eu-based-portal
        REQUIRED_URLS=(
            "https://gateway.horizon3ai.eu"
            "https://interact.gateway.horizon3ai.eu"
            "https://api.gateway.horizon3ai.eu"
            "https://registry.gateway.horizon3ai.eu"
            "https://api.horizon3ai.eu"
            "https://cognito-identity.eu-central-1.amazonaws.com"
            "https://cognito-idp.eu-central-1.amazonaws.com"
            "https://downloads.horizon3ai.com"
            "https://sqs.eu-central-1.amazonaws.com"
            # In public docs but host check fails. Need to follow up on this host.
            # "https://ineracth3t.eu" 
        )
        WILDCARD_HOSTS_443=(
            "ecr.eu-central-1.amazonaws.com"
            "queue.amazonaws.com"
            "s3.amazonaws.com"
            "s3.eu-central-1.amazonaws.com"
            "s3-w.eu-central-1.amazonaws.com"
            "s3-r-w.eu-central-1.amazonaws.com"
        )
        # Documented as http:// (port 80); checked via plain-HTTP egress, not TLS SNI
        WILDCARD_HOSTS_80=(
            "ubuntu.com"
            "canonical.com"
        )
    elif [[ "$GATEWAY_HOST" == *"horizon3ai.eu"* && "$USING_GATEWAY" == "true" ]]; then
        # EU Gateway URLs - https://docs.horizon3.ai/quickstart/network_requirements/#eu-based-nodezero-gateway
        REQUIRED_URLS=(
            "https://gateway.horizon3ai.eu"
            "https://interact.gateway.horizon3ai.eu"
            "https://api.gateway.horizon3ai.eu"
            "https://registry.gateway.horizon3ai.eu"
        )
    elif [[ "$GATEWAY_HOST" == *"gov-horizon3ai.com"* ]]; then
        # FedRAMP/Federal hosts - https://docs.horizon3.ai/quickstart/network_requirements/#fedramp-high-nodezero-gateway
        REQUIRED_URLS=(
            "https://gateway.gov-horizon3ai.com"
            "https://interact.gateway.gov-horizon3ai.com"
            "https://api.gateway.gov-horizon3ai.com"
        )
    elif [[ "$USING_GATEWAY" == "false" ]]; then
        # US Region hosts - https://docs.horizon3.ai/quickstart/network_requirements/#us-based-portal
        REQUIRED_URLS=(
            "https://gateway.horizon3ai.com"
            "https://interact.gateway.horizon3ai.com"
            "https://api.gateway.horizon3ai.com"
            "https://registry.gateway.horizon3ai.com"
            "https://api.horizon3ai.com"
            "https://cognito-identity.us-east-2.amazonaws.com"
            "https://cognito-idp.us-east-2.amazonaws.com"
            "https://downloads.horizon3ai.com"
            "https://sqs.us-east-2.amazonaws.com"
            # In public docs but host check fails. Need to follow up on this host.
            # "https://interacth3.io" 
        )
        WILDCARD_HOSTS_443=(
            "ecr.us-east-2.amazonaws.com"
            "queue.amazonaws.com"
            "s3.amazonaws.com"
            "s3.us-east-1.amazonaws.com"
            "s3.us-east-2.amazonaws.com"
            "s3-w.us-east-2.amazonaws.com"
        )
        # Documented as http:// (port 80); checked via plain-HTTP egress, not TLS SNI
        WILDCARD_HOSTS_80=(
            "ubuntu.com"
            "canonical.com"
        )
    else
        # US Region gateway hosts - https://docs.horizon3.ai/quickstart/network_requirements/#us-based-nodezero-gateway
        REQUIRED_URLS=(
            "https://gateway.horizon3ai.com"
            "https://interact.gateway.horizon3ai.com"
            "https://api.gateway.horizon3ai.com"
            "https://registry.gateway.horizon3ai.com"
            "https://api.horizon3ai.com"
        )
    fi

    GenMsg "Checking connectivity and SSL certificates for region: $GATEWAY_HOST"
    # Surface the gateway registry this pre-script uses for its connectivity probe, so a region
    # mismatch is visible in op logs (OC-2222). NOTE: this is the gateway host, NOT the ECR
    # registry the second-stage install script authenticates to (that is rendered by
    # ipt/services/scheduling's client_script.j2 and is not logged here).
    GenMsg "Gateway registry target: $REGISTRY_URL"

    for url in "${REQUIRED_URLS[@]}"; do
        # Extract host and port from URL
        host=$(echo "$url" | sed -E 's#https?://([^:/]+).*#\1#')
        port=$(echo "$url" | grep -oE ':[0-9]+' | tr -d ':')
        [[ -z "$port" ]] && port=443

        echo -ne "  Checking: $url ... "

        # First check SSL certificate using openssl
        cert_output=$(TlsConnect "$host" "$host" "$port")
        OPENSSL_EXIT=$?

        if [[ $OPENSSL_EXIT -ne 0 ]] || [[ -z "$cert_output" ]]; then
            # Connection failed - determine why using curl for better error messages
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 -m 10 "$url" 2>/dev/null)
            CURL_EXIT=$?

            if [[ $CURL_EXIT -eq 6 ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Cannot resolve hostname $host - DNS resolution failed"
                WarnMsg "  Please ensure your DNS can resolve $host or add it to your hosts file"
                WarnMsg "  See $NETWORK_DOC for network requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            elif [[ $CURL_EXIT -eq 7 ]] || [[ $CURL_EXIT -eq 28 ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Cannot connect to $host - connection refused or timed out"
                WarnMsg "  This host may be blocked by a firewall. Please ensure outbound HTTPS (443/TCP) is allowed"
                WarnMsg "  See $NETWORK_DOC for network requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            elif [[ $CURL_EXIT -eq 60 ]] || [[ $CURL_EXIT -eq 51 ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "SSL certificate issue connecting to $host - your firewall may be intercepting HTTPS traffic"
                WarnMsg "  This can happen when a corporate firewall or proxy performs SSL inspection"
                WarnMsg "  Please ensure $host is allowlisted in your firewall. See $NETWORK_DOC"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            else
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Failed to connect to $host (connection error)"
                WarnMsg "  Please verify network connectivity. See $NETWORK_DOC for requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            fi
            continue
        fi

        # Check SSL certificate expiration (hostname not verified)
        CheckCertExpiry "$cert_output" "$host"
    done

    # Wildcard hosts: NodeZero reaches dynamically-named subdomains under these
    # (e.g. <random>.s3.eu-central-1.amazonaws.com). Random labels mostly return
    # NXDOMAIN, so instead of connecting to a made-up subdomain we send a
    # wildcard-matching SNI while connecting to the resolvable parent endpoint. This
    # exercises the same SNI-based firewall/proxy rules that gate the real subdomains,
    # and passes for any subdomain because the cert hostname/SAN is not verified.
    if [[ -n "${WILDCARD_HOSTS_443[*]}" ]]; then
        PROBE_LABEL="nodezero-conntest"
        for base in "${WILDCARD_HOSTS_443[@]}"; do
            sni="${PROBE_LABEL}.${base}"
            echo -ne "  Checking: *.${base} (SNI: ${sni}) ... "

            cert_output=$(TlsConnect "$sni" "$base" 443)
            if [[ -z "$cert_output" ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Cannot establish a TLS connection for *.${base}"
                WarnMsg "  A firewall/proxy may be blocking this wildcard domain. Please ensure *.${base} is allowlisted"
                WarnMsg "  See $NETWORK_DOC for network requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
                continue
            fi

            CheckCertExpiry "$cert_output" "*.${base}"
        done
    fi

    # HTTP (port 80) hosts: NodeZero pulls OS/package content from these over plain
    # HTTP, so they are not gated by TLS SNI. We verify port-80 egress with an HTTP
    # request to the host; any HTTP response (2xx/3xx/4xx) means the connection
    # succeeded, while a firewall block yields a curl connection error or no response.
    if [[ -n "${WILDCARD_HOSTS_80[*]}" ]]; then
        for host in "${WILDCARD_HOSTS_80[@]}"; do
            echo -ne "  Checking: http://${host} (port 80) ... "

            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 -m 10 "http://${host}/" 2>/dev/null)
            CURL_EXIT=$?

            if [[ $CURL_EXIT -eq 0 ]] && [[ "$HTTP_CODE" != "000" ]]; then
                echo -e "${GREEN}PASS${NORMAL}"
            elif [[ $CURL_EXIT -eq 6 ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Cannot resolve hostname $host - DNS resolution failed"
                WarnMsg "  Please ensure your DNS can resolve $host or add it to your hosts file"
                WarnMsg "  See $NETWORK_DOC for network requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            elif [[ $CURL_EXIT -eq 7 ]] || [[ $CURL_EXIT -eq 28 ]]; then
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Cannot connect to $host on port 80 - connection refused or timed out"
                WarnMsg "  This host may be blocked by a firewall. Please ensure outbound HTTP (80/TCP) is allowed"
                WarnMsg "  See $NETWORK_DOC for network requirements"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            else
                echo -e "${RED}FAIL${NORMAL}"
                WarnMsg "Failed to connect to $host on port 80 (connection error)"
                WarnMsg "  Please ensure outbound HTTP (80/TCP) to $host is allowed. See $NETWORK_DOC"
                NETWORK_WARNINGS=$((NETWORK_WARNINGS + 1))
            fi
        done
    fi

    if [[ $NETWORK_WARNINGS -eq 0 ]]; then
        PassMsg "All required hosts are reachable with valid SSL certificates."
    else
        WarnMsg "$NETWORK_WARNINGS host(s) had connectivity or SSL certificate issues. NodeZero may not function correctly."
        WarnMsg "Please review the warnings above and ensure all required hosts are accessible."
        WarnMsg "For full network requirements, see: $NETWORK_DOC"
    fi
}


# Cleanup hello-world containers created by checks
function CleanUp {
    $CONTAINER_CMD rmi $IMAGE -f &> /dev/null
}


function AlpineCleanUp {
    $CONTAINER_CMD rmi $REGISTRY_URL/$REPOSITORY/alpine:latest -f &> /dev/null
}








# Allow clean exit by trapping ctrl+c and calling ctrl_c()
trap ctrl_c INT


function ctrl_c() {
    echo
    ExitMsg "Ctrl+C detected, exiting gracefully..."
    exit 1
}


function main {

    HeaderMsg "Conducting pre-checks to validate the environment is NodeZero ready:"

    NetworkChk
    ProxyChk
    ContainerRuntimeChk
    CONTAINERCHKRC=$?
    if [ $CONTAINERCHKRC -ne 0 ]; then
        RetryContainerRuntimeChk
    fi
    ContainerProxyCheck

    

    OsChk
    DiskChk
    MemChk
    CpuChk
    EdrChk

    HeaderMsg ${BOLD}${MAGENTA}"Pre-check validation completed successfully."${NORMAL}

    

    if [[ "$CONTAINER_RUNTIME" == "docker" ]]; then
        rm -R $DOCKER_CONFIG_PATH &> /dev/null
        rc=$?
        if [[ $rc != 0 ]]; then
            WarnMsg "Failed to remove temporary config directory $DOCKER_CONFIG_PATH, please remove manually."
        fi
    fi
}


# Beginning of script execution
main
