#!/bin/bash

# This script processes either:
#   1) All bundled conda environments in a KNIME application, OR
#   2) A single standalone conda environment
#
# It removes duplicate LC_RPATH entries from shared libraries to ensure
# compatibility with macOS 15.4 and re-signs libraries if previously signed.
#
# Usage:
#   ./fix_knime_python_envs.sh <knime_app_path_or_conda_env>
#
# Examples:
#   ./fix_knime_python_envs.sh /Applications/KNIME_5.4.3.app
#   ./fix_knime_python_envs.sh /path/to/my_conda_env
#
# Note: This script should be run on macOS.
#
# References:
#   GitHub issue:
#       https://github.com/conda-forge/numpy-feedstock/issues/347
#   The logic of this script is based on the script referred to by following comment:
#       https://github.com/conda-forge/numpy-feedstock/issues/347#issuecomment-2746317575

set -e

TARGET_PATH="$1"

# Check if TARGET_PATH is provided
if [ -z "$TARGET_PATH" ]; then
    echo "Usage: $0 <knime_app_path_or_conda_env>"
    exit 1
fi

# Verify that required tools are installed
for cmd in otool install_name_tool codesign; do
    if ! command -v "$cmd" &>/dev/null; then
        echo "Error: '$cmd' is required but not installed or not in PATH." >&2
        echo "Please install Xcode or the Command Line Tools for Xcode and run 'xcode-select --install'." >&2
        exit 1
    fi
done

# Removes duplicate LC_RPATH entries from a given library file.
# Returns 1 if no changes were made, 0 if duplicates were removed.
delete_duplicate_rpaths_in_library() {
    local library="$1"
    local duplicates_removed=1  # 1 means "no changes" by default
    local temp_file="$(mktemp)"

    # Extract LC_RPATH entries
    local rpaths
    rpaths="$(otool -l "$library" | grep -A2 LC_RPATH | grep "path " | awk '{print $2}')"

    while read -r rpath; do
        [[ -z "$rpath" ]] && continue
        # If we've seen this RPATH before, remove it
        if grep -qx "$rpath" "$temp_file"; then
            echo
            echo "  Removing duplicate RPATH '$rpath' from $library"
            install_name_tool -delete_rpath "$rpath" "$library"
            duplicates_removed=0
        else
            echo "$rpath" >> "$temp_file"
        fi
    done <<< "$rpaths"

    rm -f "$temp_file"
    return $duplicates_removed
}

# Finds .dylib and .so files in a folder and removes duplicate RPATH entries.
# If the library was previously signed, re-sign it.
process_libraries_in_folder() {
    local folder="$1"

    # Find and process all .dylib and .so files
    find "$folder" \( -name "*.dylib" -o -name "*.so" \) | while read -r library; do
        echo -n "Checking $library..."

        local was_signed=0
        # Check if already signed
        if codesign --verify --verbose=0 "$library" &>/dev/null; then
            was_signed=1
        fi

        # Remove duplicates
        if delete_duplicate_rpaths_in_library "$library"; then
            # Re-sign if it was signed before
            if [[ "$was_signed" -eq 1 ]]; then
                echo "  Re-signing $library..."
                codesign --force --sign - "$library" || \
                    echo "  Warning: Could not re-sign $library"
            else
                echo "  Skipping re-signing for $library (was not signed)."
            fi
        else
            # Clear the current line if no changes
            echo -ne "\r\033[K"
        fi
    done
    echo "Done processing libraries in $folder"
}

###############################################################
# Main Logic: Detect if the path is a KNIME application or a  #
# standalone conda environment, and handle multiple env dirs. #
###############################################################

# 1) Ensure the path exists and is a directory.
if [ ! -d "$TARGET_PATH" ]; then
    echo "Error: $TARGET_PATH does not exist or is not a directory."
    exit 1
fi

###############################################################
# 2) Check if it's a KNIME application directory.
###############################################################

KNIME_BASE_DIR="$TARGET_PATH/Contents/Eclipse"
if [ -d "$KNIME_BASE_DIR" ]; then
    echo "Detected KNIME application at $TARGET_PATH"

    # Process any env directories found under 'Contents/Eclipse/plugins/<plugin_id>/env' (older bundling style)
    # Directly match plugins that contain 'channel' and 'bin.macosx'
    for plugin_dir in "$KNIME_BASE_DIR"/plugins/*channel*bin.macosx*; do
        # Only continue if it is a directory (avoid errors if glob expands to no match)
        [ -d "$plugin_dir" ] || continue
        if [ -d "$plugin_dir/env/lib" ]; then
            echo "Processing environment in $plugin_dir/env..."
            process_libraries_in_folder "$plugin_dir/env/lib"
        fi
    done

    # Process any envs found in 'envs' directories (newer bundling style)
    if [ -d "$KNIME_BASE_DIR/bundling/envs" ]; then
        for subenv in "$KNIME_BASE_DIR/bundling/envs"/*; do
            if [ -d "$subenv/lib" ]; then
                echo "Processing environment in $subenv..."
                process_libraries_in_folder "$subenv/lib"
            fi
        done
    fi

    exit 0
fi

###############################################################
# 3) Otherwise, assume it might be a single conda environment.
###############################################################
if [ -d "$TARGET_PATH/lib" ]; then
    echo "Detected conda environment at $TARGET_PATH"
    process_libraries_in_folder "$TARGET_PATH/lib"
    exit 0
fi

###############################################################
# 4) If none of the above conditions matched, print an error.
###############################################################
echo "Error: $TARGET_PATH is neither a KNIME app (with 'Contents/Eclipse') nor a conda env (missing 'lib' folder)."
exit 1
