#!/bin/bash

# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
# or more contributor license agreements. Licensed under the Elastic License 2.0; you may not use
# this file except in compliance with the Elastic License 2.0.
{%- import_yaml 'elasticfleet/defaults.yaml' as ELASTICFLEETDEFAULTS %}
{% set SUB = salt['pillar.get']('elasticfleet:config:subscription_integrations', default=false) %}
{% set AUTO_UPGRADE_INTEGRATIONS = salt['pillar.get']('elasticfleet:config:auto_upgrade_integrations', default=false) %}
{%- set SUPPORTED_PACKAGES = salt['pillar.get']('elasticfleet:packages', default=ELASTICFLEETDEFAULTS.elasticfleet.packages, merge=True) %}

. /usr/sbin/so-common
. /usr/sbin/so-elastic-fleet-common

# Check that /opt/so/state/estemplates.txt exists to signal that Elasticsearch
#  has completed its first run of core-only integrations/indices/components/ilm
STATE_FILE_SUCCESS=/opt/so/state/estemplates.txt
INSTALLED_PACKAGE_LIST=/tmp/esfleet_installed_packages.json
BULK_INSTALL_PACKAGE_LIST=/tmp/esfleet_bulk_install.json
BULK_INSTALL_PACKAGE_TMP=/tmp/esfleet_bulk_install_tmp.json
BULK_INSTALL_OUTPUT=/opt/so/state/esfleet_bulk_install_results.json
INTEGRATION_PACKAGE_COMPONENTS=/opt/so/state/esfleet_package_components.json
INPUT_PACKAGE_COMPONENTS=/opt/so/state/esfleet_input_package_components.json
CONTENT_PACKAGE_COMPONENTS=/opt/so/state/esfleet_content_package_components.json
COMPONENT_TEMPLATES=/opt/so/state/esfleet_component_templates.json

PENDING_UPDATE=false

# Integrations which are included in the package registry, but excluded from automatic installation via this script.
#   Requiring some level of manual Elastic Stack configuration before installation
EXCLUDED_INTEGRATIONS=('apm')

version_conversion(){
    version=$1
    echo "$version" | awk -F '.' '{ printf("%d%03d%03d\n", $1, $2, $3); }'
}

compare_versions() {
    version1=$1
    version2=$2

    # Convert versions to numbers
    num1=$(version_conversion "$version1")
    num2=$(version_conversion "$version2")

    # Compare using bc
    if (( $(echo "$num1 < $num2" | bc -l) )); then
        echo "less"
    elif (( $(echo "$num1 > $num2" | bc -l) )); then
        echo "greater"
    else
        echo "equal"
    fi
}

IFS=$'\n'
agent_policies=$(elastic_fleet_agent_policy_ids)
if [ $? -ne 0 ]; then
    echo "Error: Failed to retrieve agent policies."
    exit 1
fi

default_packages=({% for pkg in SUPPORTED_PACKAGES %}"{{ pkg }}"{% if not loop.last %} {% endif %}{% endfor %})

in_use_integrations=()

for AGENT_POLICY in $agent_policies; do

    if ! integrations=$(elastic_fleet_integration_policy_names "$AGENT_POLICY"); then
        # skip the agent policy if we can't get required info, let salt retry. Integrations loaded by this script are non-default integrations.
        echo "Skipping $AGENT_POLICY.. "
        continue
    fi
    for INTEGRATION in $integrations; do
        if ! PACKAGE_NAME=$(elastic_fleet_integration_policy_package_name "$AGENT_POLICY" "$INTEGRATION"); then
            echo  "Not adding $INTEGRATION, couldn't get package name"
            continue
        fi
        # non-default integrations that are in-use in any policy
        if ! [[ " ${default_packages[@]} " =~ " $PACKAGE_NAME " ]]; then
            in_use_integrations+=("$PACKAGE_NAME")
        fi
    done
done

if [[ -f $STATE_FILE_SUCCESS  ]]; then
    if retry 3 1 "curl -s -K /opt/so/conf/elasticsearch/curl.config --output /dev/null --silent --head --fail localhost:5601/api/fleet/epm/packages"; then
        # Package_list contains all integrations beta / non-beta.
        latest_package_list=$(/usr/sbin/so-elastic-fleet-package-list)
        echo '{ "packages" : []}' > $BULK_INSTALL_PACKAGE_LIST
        rm -f $INSTALLED_PACKAGE_LIST
        echo $latest_package_list | jq '{packages: [.items[] | {name: .name, latest_version: .version, installed_version: .installationInfo.version, subscription: .conditions.elastic.subscription }]}' >> $INSTALLED_PACKAGE_LIST

        while read -r package; do
            # get package details
            package_name=$(echo "$package" | jq -r '.name')
            latest_version=$(echo "$package" | jq -r '.latest_version')
            installed_version=$(echo "$package" | jq -r '.installed_version')
            subscription=$(echo "$package" | jq -r '.subscription')
            bulk_package=$(echo "$package" | jq '{name: .name, version: .latest_version}' )

            if [[ ! "${EXCLUDED_INTEGRATIONS[@]}" =~ "$package_name" ]]; then
            {% if not SUB %}
                if [[ "$subscription" != "basic" && "$subscription" != "null" && -n "$subscription" ]]; then
                    # pass over integrations that require non-basic elastic license
                    echo "$package_name integration requires an Elastic license of $subscription or greater... skipping"
                    continue
                else
                    if [[ "$installed_version" == "null" || -z "$installed_version" ]]; then
                        echo "$package_name is not installed... Adding to next update."
                        jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST

                        PENDING_UPDATE=true
                    else
                        results=$(compare_versions "$latest_version" "$installed_version")
                        if [ $results == "greater" ]; then
                            {#- When auto_upgrade_integrations is false, skip upgrading in_use_integrations  #}
                            {%- if not AUTO_UPGRADE_INTEGRATIONS %}
                            if ! [[ " ${in_use_integrations[@]} " =~ " $package_name " ]]; then
                            {%- endif %}
                                echo "$package_name is at version $installed_version latest version is $latest_version... Adding to next update."
                                jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST

                                PENDING_UPDATE=true
                            {%- if not AUTO_UPGRADE_INTEGRATIONS %}
                            else
                                echo "skipping available upgrade for in use integration - $package_name."
                            fi
                            {%- endif %}
                        fi
                    fi
                fi
            {% else %}
                if [[ "$installed_version" == "null" || -z "$installed_version" ]]; then
                    echo "$package_name is not installed... Adding to next update."
                    jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
                    PENDING_UPDATE=true
                else
                    results=$(compare_versions "$latest_version" "$installed_version")
                    if [ $results == "greater" ]; then
                        {#- When auto_upgrade_integrations is false, skip upgrading in_use_integrations  #}
                        {%- if not AUTO_UPGRADE_INTEGRATIONS %}
                        if ! [[ " ${in_use_integrations[@]} " =~ " $package_name " ]]; then
                        {%- endif %}
                            echo "$package_name is at version $installed_version latest version is $latest_version... Adding to next update."
                            jq --argjson package "$bulk_package" '.packages += [$package]' $BULK_INSTALL_PACKAGE_LIST > $BULK_INSTALL_PACKAGE_TMP && mv $BULK_INSTALL_PACKAGE_TMP $BULK_INSTALL_PACKAGE_LIST
                            PENDING_UPDATE=true
                        {%- if not AUTO_UPGRADE_INTEGRATIONS %}
                        else
                            echo "skipping available upgrade for in use integration - $package_name."
                        fi
                        {%- endif %}
                    fi
                fi
            {% endif %}
            else
                echo "Skipping $package_name..."
            fi
        done <<< "$(jq -c '.packages[]' "$INSTALLED_PACKAGE_LIST")"

        if [ "$PENDING_UPDATE" = true ]; then
            # Run chunked install of packages
            echo "" > $BULK_INSTALL_OUTPUT
            pkg_group=1
            pkg_filename="${BULK_INSTALL_PACKAGE_LIST%.json}"

            jq -c '.packages | _nwise(25)' $BULK_INSTALL_PACKAGE_LIST | while read -r line; do
                echo "$line" | jq '{ "packages": . }' > "${pkg_filename}_${pkg_group}.json"
                pkg_group=$((pkg_group + 1))
            done

            for file in "${pkg_filename}_"*.json; do
                [ -e "$file" ] || continue
                if ! elastic_fleet_bulk_package_install $file >> $BULK_INSTALL_OUTPUT; then
                    # integrations loaded my this script are non-essential and shouldn't cause exit, skip them for now next highstate run can retry
                    echo "Failed to complete a chunk of bulk package installs -- $file "
                    continue
                fi
            done
            # cleanup any temp files for chunked package install
            rm -f ${pkg_filename}_*.json $BULK_INSTALL_PACKAGE_LIST
        else
            echo "Elastic integrations don't appear to need installation/updating..."
        fi
        # Write out file for generating index/component/ilm templates, keeping each package type separate
        for package_type in "INTEGRATION" "INPUT" "CONTENT"; do
            if latest_installed_package_list=$(elastic_fleet_installed_packages_components "$package_type"); then
                outfile="${package_type}_PACKAGE_COMPONENTS"
                echo $latest_installed_package_list > "${!outfile}"
            fi
        done
        if retry 3 1 "so-elasticsearch-query / --fail --output /dev/null"; then
            # Refresh installed component template list
            latest_component_templates_list=$(so-elasticsearch-query _component_template | jq '.component_templates[] | .name' | jq -s '.')
            echo $latest_component_templates_list > $COMPONENT_TEMPLATES
        fi

    else
        # This is the installation of add-on integrations and upgrade of existing integrations. Exiting without error, next highstate will attempt to re-run.
        echo "Elastic Fleet does not appear to be responding... Exiting... "
        exit 0
    fi
else
    # This message will appear when an update to core integration is made and this script is run at the same time as
    #  elasticsearch.enabled -> detects change to core index settings -> deletes estemplates.txt
    echo "Elasticsearch may not be fully configured yet or is currently updating core index settings."
    exit 0
fi
