#!/bin/bash -eu

: "${REF:="/usr/share/jenkins/ref"}"

# Substitute environment variables in file content
# Supports ${VAR} and ${VAR:-default} syntax
substitute_env_vars() {
    local file="$1"
    local content
    content=$(cat "${file}")
    
    # Loop until no more substitutions found
    while [[ "${content}" =~ \$\{([A-Za-z_][A-Za-z0-9_]*)(:-([^}]*))?\} ]]; do
        local var_name="${BASH_REMATCH[1]}"
        local default_value="${BASH_REMATCH[3]}"
        local full_match="${BASH_REMATCH[0]}"
        
        # Get variable value using indirect expansion, or use default if empty
        local var_value="${!var_name:-}"
        if [[ -z "${var_value}" && -n "${default_value}" ]]; then
            var_value="${default_value}"
        fi
        
        # Replace all occurrences
        content="${content//${full_match}/${var_value}}"
    done
    
    echo "${content}" > "${file}"
}

# compare if version1 < version2
versionLT() {
    local normalized_version1 normalized_version2 first_part_of_1 other_part_of_1 first_char_other_part_of_1 first_part_of_2 other_part_of_2 first_char_other_part_of_2

    # Quick check for equality
    if [ "$1" = "$2" ]; then
        return 1
    fi

    # Convert '-' to '.' to ease comparison
    normalized_version1=$(echo "$1" | tr '-' '.')
    normalized_version2=$(echo "$2" | tr '-' '.')
    first_part_of_1=${normalized_version1%%.*}
    other_part_of_1=${normalized_version1#*.}
    first_char_other_part_of_1=${other_part_of_1:0:1}
    first_part_of_2=${normalized_version2%%.*}
    other_part_of_2=${normalized_version2#*.}
    first_char_other_part_of_2=${other_part_of_2:0:1}


    # Security fix backport / maintenance-branch special case.
    # Ex: 3894.vd0f0248b_a_fc4 < 3894.3896.vca_2c931e7935
    # -> normal incrementals version includes a "v" as first char of second part
    # -> security fix backport adds the backport source first part as second part
    # https://github.com/jenkinsci/workflow-cps-plugin/releases/tag/3894.vd0f0248b_a_fc4
    # https://github.com/jenkinsci/workflow-cps-plugin/releases/tag/3894.3896.vca_2c931e7935
    #
    # Whenever the first parts match and exactly one of the two "second parts"
    # starts with "v", that side is the older one regardless of argument order.
    # The check has to be symmetric: copy_reference_file() calls versionLT()
    # with the image version as $1 and the installed version as $2, so the
    # original one-sided check missed the "image is the 4-part backport,
    # installed is the 3-part normal release" direction. In that direction the
    # function would otherwise fall through to `sort --version-sort`, which
    # ranks the older "<base>.v<sha>" form *after* the newer
    # "<base>.<buildnum>.v<sha>" form (because "v" sorts after digits in
    # ASCII), and the entrypoint would silently skip the upgrade.
    # Tracked at https://github.com/jenkinsci/docker/issues/2328
    if [[ "$first_part_of_1" = "$first_part_of_2" ]]; then
        if [[ "$first_char_other_part_of_1" == "v" && "$first_char_other_part_of_2" != "v" ]]; then
            return 0
        fi
        if [[ "$first_char_other_part_of_2" == "v" && "$first_char_other_part_of_1" != "v" ]]; then
            return 1
        fi
    fi

    if [ "$normalized_version1" = "$(printf '%s\n%s\n' "$normalized_version1" "$normalized_version2" | sort --version-sort | head -n1)" ]; then
        return 0
    else
        return 1
    fi
}

# returns a plugin version from a plugin archive
get_plugin_version() {
    local archive; archive=$1
    local version; version=$(unzip -p "$archive" META-INF/MANIFEST.MF | grep "^Plugin-Version: " | sed -e 's#^Plugin-Version: ##')
    version=${version%%[[:space:]]}
    echo "$version"
}

# Copy files from /usr/share/jenkins/ref into $JENKINS_HOME
# So the initial JENKINS-HOME is set with expected content.
# Don't override, as this is just a reference setup, and use from UI
# can then change this, upgrade plugins, etc.
copy_reference_file() {
    f="${1%/}"
    b="${f%.override}"
    rel="${b#"$REF/"}"
    version_marker="${rel}.version_from_image"
    dir=$(dirname "${rel}")
    local action;
    local reason;
    local container_version;
    local image_version;
    local marker_version;
    local log; log=false
    if [[ ${rel} == plugins/*.jpi ]]; then
        container_version=$(get_plugin_version "$JENKINS_HOME/${rel}")
        image_version=$(get_plugin_version "${f}")
        if [[ -e $JENKINS_HOME/${version_marker} ]]; then
            marker_version=$(cat "$JENKINS_HOME/${version_marker}")
            if versionLT "$marker_version" "$container_version"; then
                if ( versionLT "$container_version" "$image_version" && [[ -n $PLUGINS_FORCE_UPGRADE ]]); then
                    action="UPGRADED"
                    reason="Manually upgraded version ($container_version) is older than image version $image_version"
                    log=true
                else
                    action="SKIPPED"
                    reason="Installed version ($container_version) has been manually upgraded from initial version ($marker_version)"
                    log=true
                fi
            else
                if [[ "$image_version" == "$container_version" ]]; then
                    action="SKIPPED"
                    reason="Version from image is the same as the installed version $image_version"
                else
                    if versionLT "$image_version" "$container_version"; then
                        action="SKIPPED"
                        log=true
                        reason="Image version ($image_version) is older than installed version ($container_version)"
                    else
                        action="UPGRADED"
                        log=true
                        reason="Image version ($image_version) is newer than installed version ($container_version)"
                    fi
                fi
            fi
        else
            if [[ -n "$TRY_UPGRADE_IF_NO_MARKER" ]]; then
                if [[ "$image_version" == "$container_version" ]]; then
                    action="SKIPPED"
                    reason="Version from image is the same as the installed version $image_version (no marker found)"
                    # Add marker for next time
                    echo "$image_version" > "$JENKINS_HOME/${version_marker}"
                else
                    if versionLT "$image_version" "$container_version"; then
                        action="SKIPPED"
                        log=true
                        reason="Image version ($image_version) is older than installed version ($container_version) (no marker found)"
                    else
                        action="UPGRADED"
                        log=true
                        reason="Image version ($image_version) is newer than installed version ($container_version) (no marker found)"
                    fi
                fi
            fi
        fi
        if [[ ! -e $JENKINS_HOME/${rel} || "$action" == "UPGRADED" || $f = *.override ]]; then
            action=${action:-"INSTALLED"}
            log=true
            mkdir -p "$JENKINS_HOME/${dir}"
            cp -pr "${f}" "$JENKINS_HOME/${rel}";
            # pin plugins on initial copy
            touch "$JENKINS_HOME/${rel}.pinned"
            echo "$image_version" > "$JENKINS_HOME/${version_marker}"
            reason=${reason:-$image_version}
        else
            action=${action:-"SKIPPED"}
        fi
    else
        if [[ ! -e $JENKINS_HOME/${rel} || $f = *.override ]]
        then
            action="INSTALLED"
            log=true
            mkdir -p "$JENKINS_HOME/${dir}"
            cp -pr "$(realpath "${f}")" "$JENKINS_HOME/${rel}";
            
            # Perform environment variable substitution on config files (opt-in)
            if [[ "${JENKINS_ENABLE_ENV_SUBST:-false}" == "true" ]]; then
                if [[ "$rel" == *.xml || "$rel" == *.conf || "$rel" == *.properties || "$rel" == *.groovy ]]; then
                    if [[ -f "$JENKINS_HOME/${rel}" ]]; then
                        substitute_env_vars "$JENKINS_HOME/${rel}"
                    fi
                fi
            fi
        else
            action="SKIPPED"
        fi
    fi
    if [[ -n "$VERBOSE" || "$log" == "true" ]]; then
        if [ -z "$reason" ]; then
            echo "$action $rel" >> "$COPY_REFERENCE_FILE_LOG"
        else
            echo "$action $rel : $reason" >> "$COPY_REFERENCE_FILE_LOG"
        fi
    fi
}
