#!/usr/bin/env bash
# ********* Starting configuration *********
while getopts ":m:p:s:t:l:g:n:xh"  option; do
case "${option}" in
m) HOME_DIR=${OPTARG};;
p) PROXY_URL=${OPTARG};;
s) SERVER_ID=${OPTARG};;
t) TELEMETRY_PORT=${OPTARG};;
l) TELEMETRY_LOGS_LEVEL=${OPTARG};;
g) TRACING_PORT=${OPTARG};;
n) INSTALLATION_NAME=${OPTARG};;
x) SKIP_SERVICE="true";;
h)
    echo "DESCRIPTION: Install Anypoint Monitoring OpenTelemetry Collector"
    echo "Usage: $0 [options]"
    echo " "
    echo "options:"
    echo "-h,       show help"
    echo "-m,       set home directory - defaults to MULE_HOME"
    echo "-p,       set HTTP/HTTPS proxy server URL"
    echo "          Example: -p https://user:password@https-server:1080"
    echo "-t,       set internal telemetry port - defaults to 8888"
    echo "-l,       set internal telemetry logs level (info, debug, warn, error, off) - defaults to info"
    echo "-g,       set tracing port - defaults disabled"
    echo "-n,       optional installation name (<=8 chars, alphanumeric only) for multi-install services"
    echo " "
    exit 0
    ;;
esac
done
shift "$((OPTIND-1))"

if [[ -z "$HOME_DIR" || ! -d "$HOME_DIR" ]]; then
    if [[ -d "$MULE_HOME" ]]; then
        HOME_DIR=$MULE_HOME
    else
        cd $(dirname $0)
        cd ../..
        HOME_DIR=`pwd`
    fi
fi

echo "MULE_HOME is set to ${HOME_DIR}"

# If prior name exists and -n is missing/different: prompt to reuse, else exit.
AM_HOME=${HOME_DIR}/am
EXISTING_INSTALLATION_NAME_FILE="${AM_HOME}/config/installation.name"
if [[ -f "$EXISTING_INSTALLATION_NAME_FILE" ]]; then
    EXISTING_INSTALLATION_NAME="$(cat "$EXISTING_INSTALLATION_NAME_FILE" 2>/dev/null | tr -d '[:space:]')"
    if [[ -n "$EXISTING_INSTALLATION_NAME" ]]; then
        if [[ -z "$INSTALLATION_NAME" || "$INSTALLATION_NAME" != "$EXISTING_INSTALLATION_NAME" ]]; then
            read -p "Found existing installation name '${EXISTING_INSTALLATION_NAME}'. Reuse it? [y|n] " -n 1 -r
            echo
            if [[ $REPLY =~ ^[Yy]$ ]]; then
                INSTALLATION_NAME="$EXISTING_INSTALLATION_NAME"
                REUSING_EXISTING_INSTALLATION_NAME="true"
            else
                echo "ERROR: Please run '${AM_HOME}/bin/uninstall' first, then re-run install."
                exit 1
            fi
        fi
    fi
fi

# Validate and persist installation name if provided
if [[ -n "$INSTALLATION_NAME" ]]; then
    if [[ ! "$INSTALLATION_NAME" =~ ^[A-Za-z0-9]{1,8}$ ]]; then
        echo "ERROR: installation name must be 1-8 alphanumeric characters."
        exit 1
    fi
    # If not reusing, ensure the name isn't already in use.
    if [[ -z "$REUSING_EXISTING_INSTALLATION_NAME" ]]; then
        NAME_PREFIX_CHECK="${INSTALLATION_NAME}-"
        if [[ -f "/lib/systemd/system/${NAME_PREFIX_CHECK}am-otel-collector.service" ]] || \
           [[ -f "/lib/systemd/system/${NAME_PREFIX_CHECK}am-directory-watcher.service" ]] || \
           [[ -f "/usr/lib/systemd/system/${NAME_PREFIX_CHECK}am-otel-collector.service" ]] || \
           [[ -f "/usr/lib/systemd/system/${NAME_PREFIX_CHECK}am-directory-watcher.service" ]] || \
           [[ -f "/etc/otel-collector/${NAME_PREFIX_CHECK}am-agent.pid" ]] || \
           [[ -f "/etc/directory-watcher/${NAME_PREFIX_CHECK}directory-watcher.pid" ]]; then
            echo "ERROR: installation name '${INSTALLATION_NAME}' is already in use. Please choose a different name with -n (<=8 chars, alphanumeric only)."
            exit 1
        fi
    fi
fi

# Detect JAVA_HOME if not provided by user
if [[ -z "$JAVA_HOME" ]]; then
    # Try to find Java in common locations
    if command -v java >/dev/null 2>&1; then
        # Java is in PATH, find its installation directory
        JAVA_PATH=$(which java)
        if [[ -L "$JAVA_PATH" ]]; then
            # Resolve symlink
            JAVA_PATH=$(readlink -f "$JAVA_PATH")
        fi
        # Go up two directories from bin/java to get JAVA_HOME
        JAVA_HOME=$(dirname "$(dirname "$JAVA_PATH")")
        echo "Auto-detected JAVA_HOME: $JAVA_HOME"
    else
        echo "Warning: Could not auto-detect JAVA_HOME. Please set JAVA_HOME environment variable."
        echo "Example: export JAVA_HOME=/path/to/java/home"
        JAVA_HOME=""
        exit 1
    fi
else
    echo "Using user-provided JAVA_HOME: $JAVA_HOME"
fi

AM_HOME=${HOME_DIR}/am
source ${AM_HOME}/bin/tools/serviceHelper
source ${AM_HOME}/bin/tools/pid-file
PID_NAME_PREFIX=""
SERVICE_NAME_PREFIX=""
if [[ -n "$INSTALLATION_NAME" ]]; then
    PID_NAME_PREFIX="${INSTALLATION_NAME}-"
    SERVICE_NAME_PREFIX="${INSTALLATION_NAME}-"
    # Persist for later scripts/services
    echo -n "$INSTALLATION_NAME" > "${AM_HOME}/config/installation.name"
else
    # No installation name provided: ensure default (unprefixed) behavior by clearing any persisted name
    rm -f "${AM_HOME}/config/installation.name" > /dev/null 2>&1
    # Safety: if default (unprefixed) services already exist, prevent accidental overwrite and require -n
    if [[ -f "/lib/systemd/system/am-otel-collector.service" ]] || \
       [[ -f "/lib/systemd/system/am-directory-watcher.service" ]] || \
       [[ -f "/usr/lib/systemd/system/am-otel-collector.service" ]] || \
       [[ -f "/usr/lib/systemd/system/am-directory-watcher.service" ]] || \
       [[ -f "/etc/otel-collector/am-agent.pid" ]] || \
       [[ -f "/etc/directory-watcher/directory-watcher.pid" ]]; then
        echo "ERROR: A default AM service installation already exists on this host."
        echo "Please re-run install with -n <installation_name> to create an additional instance (<=8 chars, alphanumeric only)."
        exit 1
    fi
fi

installServiceAgent() {
    # Configuring otel collector as a service
    local otel_service_name="${SERVICE_NAME_PREFIX}am-otel-collector.service"
    local dw_service_name="${SERVICE_NAME_PREFIX}am-directory-watcher.service"
    SERVICE_UNIT_FILE="/lib/systemd/system/${otel_service_name}"
    DIRECTORY_WATCHER_SERVICE_UNIT_FILE="/lib/systemd/system/${dw_service_name}"
    
    [[ -f /bin/systemctl ]] && SYSTEMCTL_EXEC=/bin/systemctl || SYSTEMCTL_EXEC=/usr/bin/systemctl
    if [[ ! -d "$SERVICE_UNIT_FILE" ]]; then
        echo "Copying otel collector service configuration"
        run_as_root cp ${AM_HOME}/config/am-otel-collector.template.service $SERVICE_UNIT_FILE
        run_as_root sed -i '' -e "s/\$muleHome\\$/${HOME_DIR//\//\\/}/g" $SERVICE_UNIT_FILE > /dev/null 2>&1
        run_as_root sed -i '' -e "s/\$javaHome\\$/${JAVA_HOME//\//\\/}/g" $SERVICE_UNIT_FILE > /dev/null 2>&1
        GOMEMLIMIT=26MiB
        run_as_root sed -i '' -e "s/\$gomemlimit\\$/${GOMEMLIMIT//\//\\/}/g" $SERVICE_UNIT_FILE > /dev/null 2>&1
        run_as_root sed -i '' -e "s/\$pidPrefix\\$/${PID_NAME_PREFIX//\//\\/}/g" $SERVICE_UNIT_FILE > /dev/null 2>&1
        run_as_root sed -i '' -e "s/\$installName\\$/${INSTALLATION_NAME//\//\\/}/g" $SERVICE_UNIT_FILE > /dev/null 2>&1
    fi

    # Process DirectoryWatcher service configuration
    if [[ ! -d "$DIRECTORY_WATCHER_SERVICE_UNIT_FILE" ]]; then
        echo "Copying directory watcher service configuration"
        run_as_root cp ${AM_HOME}/config/am-directory-watcher.template.service $DIRECTORY_WATCHER_SERVICE_UNIT_FILE
        run_as_root sed -i '' -e "s/\$muleHome\\$/${HOME_DIR//\//\\/}/g" $DIRECTORY_WATCHER_SERVICE_UNIT_FILE > /dev/null 2>&1
        
        # Replace javaHome placeholder if JAVA_HOME is available
        if [[ -n "$JAVA_HOME" ]]; then
            run_as_root sed -i '' -e "s/\$javaHome\\$/${JAVA_HOME//\//\\/}/g" $DIRECTORY_WATCHER_SERVICE_UNIT_FILE > /dev/null 2>&1
            echo "DirectoryWatcher service configured with JAVA_HOME: $JAVA_HOME"
        else
            echo "Warning: DirectoryWatcher service installed but JAVA_HOME not set"
        fi
        run_as_root sed -i '' -e "s/\$pidPrefix\\$/${PID_NAME_PREFIX//\//\\/}/g" $DIRECTORY_WATCHER_SERVICE_UNIT_FILE > /dev/null 2>&1
        run_as_root sed -i '' -e "s/\$installName\\$/${INSTALLATION_NAME//\//\\/}/g" $DIRECTORY_WATCHER_SERVICE_UNIT_FILE > /dev/null 2>&1
    fi

    # Create pid file to run as a service in sudo directory
    PID_NAME_PREFIX="${PID_NAME_PREFIX}" createOtelCollectorPidFile true /etc/otel-collector
    PID_NAME_PREFIX="${PID_NAME_PREFIX}" createDirectoryWatcherPidFile true /etc/directory-watcher
    run_as_root systemctl daemon-reload
}

configureProxyUrl() {
    PROXY_URL_FILE=${AM_HOME}/config/proxy.url
    # Remove existing proxy url file
    if [[ -f ${PROXY_URL_FILE} ]]; then
        rm ${PROXY_URL_FILE}
    fi
    # Check and write the proxy URL file if PROXY_URL or HTTPS_PROXY is set
    local proxy_value="${PROXY_URL:-$HTTPS_PROXY}"
    if [[ -n "$proxy_value" ]]; then
        printf "%s\n" "$proxy_value" > "${PROXY_URL_FILE}"
        printf "Set the proxy to %s.\n" "$proxy_value"
    fi
}

# helper: returns 0 if free, 1 if in use or unknown
is_port_free() {
    local _port="$1"
    if command -v lsof >/dev/null 2>&1; then
        # Try without sudo first
        if lsof -nP -iTCP:"${_port}" -sTCP:LISTEN >/dev/null 2>&1; then
            return 1
        fi
        # Then try with elevated privileges (non-interactive); detects root-owned listeners
        if run_as_root lsof -nP -iTCP:"${_port}" -sTCP:LISTEN >/dev/null 2>&1; then
            return 1
        fi
    elif command -v ss >/dev/null 2>&1; then
        ss -ltn 2>/dev/null | awk '{print $4}' | grep -E "[:\.]${_port}$" >/dev/null 2>&1 && return 1 || return 0
    elif command -v netstat >/dev/null 2>&1; then
        netstat -ltn 2>/dev/null | awk '{print $4}' | grep -E "[:\.]${_port}$" >/dev/null 2>&1 && return 1 || return 0
    else
        # If we cannot determine, assume free to avoid false positives
        return 0
    fi
    # If none of the above detected usage, consider it free
    return 0
}

configureTelemetryPort() {
    TELEMETRY_PORT_FILE=${AM_HOME}/config/telemetry.port
    # Remove existing telemetry port file
    if [[ -f ${TELEMETRY_PORT_FILE} ]]; then
        rm ${TELEMETRY_PORT_FILE}
    fi
    # Write telemetry port: use provided value, else default to 8888
    if [[ -n "$TELEMETRY_PORT" ]]; then
        # Trim whitespace
        local _tp
        _tp=$(echo -n "$TELEMETRY_PORT" | tr -d '[:space:]')
        # Validate numeric and range (1024-65535)
        if [[ $_tp =~ ^[0-9]+$ ]] && (( _tp >= 1024 && _tp <= 65535 )); then
            if is_port_free "$_tp"; then
                printf "%s\n" "$_tp" > "${TELEMETRY_PORT_FILE}"
                printf "Set the telemetry port to %s.\n" "$_tp"
            else
                printf "ERROR: Telemetry port %s is already in use. Please re-run install with -t <free port>.\n"
                exit 1
            fi
        else
            printf "ERROR: Invalid telemetry port '%s'. Must be an integer between 1024 and 65535.\n" "$TELEMETRY_PORT"
            exit 1
        fi
    else
        if is_port_free "8888"; then
            printf "%s\n" "8888" > "${TELEMETRY_PORT_FILE}"
            printf "Set the telemetry port to default %s.\n" "8888"
        else
            printf "ERROR: Default telemetry port 8888 is in use. Please re-run install with -t <free port>.\n"
            exit 1
        fi
    fi
}

configureTelemetryLogs() {
    TELEMETRY_LOGS_LEVEL_FILE=${AM_HOME}/config/telemetry.logs.level
    TELEMETRY_LOGS_OUTPUT_PATH_FILE=${AM_HOME}/config/telemetry.logs.output.path
    TELEMETRY_LOGS_ERROR_PATH_FILE=${AM_HOME}/config/telemetry.logs.error.path
    TELEMETRY_LOGS_DEFAULT_OUTPUT_FILE="${AM_HOME}/logs/otel-collector.log"
    TELEMETRY_LOGS_DEFAULT_ERROR_FILE="${AM_HOME}/logs/otel-collector.err"
    # Remove existing files
    if [[ -f ${TELEMETRY_LOGS_LEVEL_FILE} ]]; then
        rm ${TELEMETRY_LOGS_LEVEL_FILE}
    fi
    if [[ -f ${TELEMETRY_LOGS_OUTPUT_PATH_FILE} ]]; then
        rm ${TELEMETRY_LOGS_OUTPUT_PATH_FILE}
    fi
    if [[ -f ${TELEMETRY_LOGS_ERROR_PATH_FILE} ]]; then
        rm ${TELEMETRY_LOGS_ERROR_PATH_FILE}
    fi
    # Write telemetry logs level: use provided value, else default to INFO
    if [[ -n "$TELEMETRY_LOGS_LEVEL" ]]; then
        # Trim whitespace
        local _tll
        _tll=$(echo -n "$TELEMETRY_LOGS_LEVEL" | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]')
        # Validate (INFO, DEBUG, WARN, ERROR, OFF)
        if [[ $_tll == "INFO" || $_tll == "DEBUG" || $_tll == "WARN" || $_tll == "ERROR" ]]; then
            printf "%s" "$_tll" > "${TELEMETRY_LOGS_LEVEL_FILE}"
            printf "%s" "$TELEMETRY_LOGS_DEFAULT_OUTPUT_FILE" > "${TELEMETRY_LOGS_OUTPUT_PATH_FILE}"
            printf "%s" "$TELEMETRY_LOGS_DEFAULT_ERROR_FILE" > "${TELEMETRY_LOGS_ERROR_PATH_FILE}"
            printf "Set the telemetry logs level to %s.\n" "$_tll"
        elif [[ $_tll == "OFF" ]]; then
            printf "INFO" > "${TELEMETRY_LOGS_LEVEL_FILE}"
            printf "%s" "/dev/null" > "${TELEMETRY_LOGS_OUTPUT_PATH_FILE}"
            printf "%s" "/dev/null" > "${TELEMETRY_LOGS_ERROR_PATH_FILE}"
            printf "Set the telemetry logs level to %s.\n" "$_tll"
        else
            printf "ERROR: Invalid telemetry logs level '%s'. Must be INFO, DEBUG, WARN, ERROR, or OFF.\n" "$TELEMETRY_LOGS_LEVEL"
            exit 1
        fi
    else
        printf "%s" "INFO" > "${TELEMETRY_LOGS_LEVEL_FILE}"
        printf "%s" "$TELEMETRY_LOGS_DEFAULT_OUTPUT_FILE" > "${TELEMETRY_LOGS_OUTPUT_PATH_FILE}"
        printf "%s" "$TELEMETRY_LOGS_DEFAULT_ERROR_FILE" > "${TELEMETRY_LOGS_ERROR_PATH_FILE}"
        printf "Set the telemetry logs level to default %s.\n" "INFO"
    fi
}

configureTracingPort() {
    TRACING_PIPELINE_FILE=${AM_HOME}/config/pipelines/_trace.yml
    # Remove existing tracing files
    if [[ -f ${TRACING_PIPELINE_FILE} ]]; then
        rm ${TRACING_PIPELINE_FILE}
    fi
    # Write tracing port: use provided value, else default disabled
    if [[ -n "$TRACING_PORT" ]]; then
        # Trim whitespace
        local _tp
        _tp=$(echo -n "$TRACING_PORT" | tr -d '[:space:]')
        # Validate numeric and range (1024-65535)
        if [[ $_tp =~ ^[0-9]+$ ]] && (( _tp >= 1024 && _tp <= 65535 )); then
            if is_port_free "$_tp"; then
                cp ${AM_HOME}/config/trace-pipeline-template.yml $TRACING_PIPELINE_FILE
                sed -i '' -e "s/\$tracingPort\\$/${_tp//\//\\/}/g" $TRACING_PIPELINE_FILE > /dev/null 2>&1
                printf "Set the tracing port to %s.\n" "$_tp"
            else
                printf "ERROR: Tracing port %s is already in use. Please re-run install with -g <free port>.\n"
                exit 1
            fi
        else
            printf "ERROR: Invalid tracing port '%s'. Must be an integer between 1024 and 65535.\n" "$TRACING_PORT"
            exit 1
        fi
    fi
}

configureAgent() {
    echo "Configuring agent..."
    if (type -p java) || ([[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]); then
        java -jar ${AM_HOME}/lib/agent-configurator.jar ${HOME_DIR}
    else
        echo "Error pushing configuration because Java is not installed"
    fi
}

rm -f ${AM_HOME}/config/pipelines/*.yml > /dev/null 2>&1
if [[ -z "$SERVER_ID" ]]; then
    rm ${AM_HOME}/config/server.id > /dev/null 2>&1
else
    touch ${AM_HOME}/config/server.id > /dev/null 2>&1
    echo "$SERVER_ID" > "${AM_HOME}/config/server.id"
fi

# Create pid file in home
PID_NAME_PREFIX="${PID_NAME_PREFIX}" createOtelCollectorPidFile false ${AM_HOME}
PID_NAME_PREFIX="${PID_NAME_PREFIX}" createDirectoryWatcherPidFile false ${AM_HOME}
configureProxyUrl
configureTelemetryPort
configureTelemetryLogs
configureTracingPort
configureAgent

if [[ -z "$SKIP_SERVICE" ]]; then
    if [[ "$OSTYPE" != "darwin"* ]]; then
        read -p "Do you want to use monitoring as a service? [y|n] " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            # Warn if services are already installed
            if [[ -f "/lib/systemd/system/${SERVICE_NAME_PREFIX}am-otel-collector.service" ]]; then
                echo "WARNING: Service '${SERVICE_NAME_PREFIX}am-otel-collector' is already installed. It will be reused/updated."
            fi
            if [[ -f "/lib/systemd/system/${SERVICE_NAME_PREFIX}am-directory-watcher.service" ]]; then
                echo "WARNING: Service '${SERVICE_NAME_PREFIX}am-directory-watcher' is already installed. It will be reused/updated."
            fi
            echo "Installing Anypoint Monitoring OpenTelemetry Collector as a service..."
            installServiceAgent
        fi
        echo "Next Steps:"
        echo "1. Restart your Mule server completely to ensure all configurations are loaded properly"
        echo "2. After restarting Mule, run the setup script to start monitoring:"
        echo "   $AM_HOME/bin/setup"
    fi
else
  echo "Skipping service installation prompt. Anypoint Monitoring OpenTelemetry Collector will run as a standalone process."
fi

exit 0
