#!/usr/bin/env bash

# These values are based on the `target_host` values
# set when buildin the substrate
x86_platform="x86_64-darwin"
arm64_platform="arm64-darwin"

csource="${BASH_SOURCE[0]}"
while [ -h "$csource" ] ; do csource="$(readlink "$csource")"; done
root="$( cd -P "$( dirname "$csource" )/../../" && pwd )"

. "${root}/package/common-setup"

if [ "${#}" -ne 2 ]; then
    printf "Usage: %s SUBSTRATE-FILE VAGRANT-GEM" "${0}" >&2
    exit 1
fi

# Remember our current working directory
cwd="$(pwd)" || exit

# Grab the parameter values
substrate_file="${1}"
vagrant_gem="${2}"

substrate_file="$(file_directory "${substrate_file}")/${substrate_file##*/}"
vagrant_gem="$(file_directory "${vagrant_gem}")/${vagrant_gem##*/}"

# Validate the provided values
if [ ! -f "${substrate_file}" ]; then
    error "Invalid path provided for Vagrant substrate (%s)" "${substrate_file}"
fi

if [ ! -f "${vagrant_gem}" ]; then
    error "Invalid path provided for Vagrant gem (%s)" "${vagrant_gem}"
fi

# Make sure the go modules are installed
pushd "${root}" > /dev/null || exit
go mod download || exit
popd > /dev/null || exit

# Create a working directory for unpacking
# the substrate
substrate_dir="$(mktemp -d vagrant-substrate.XXXXXX)" || exit

# Hop in and unpack our substrate
info "Unpacking substrate..."
pushd "${substrate_dir}" > /dev/null || exit
# Grab the full path while we are here
substrate_dir="$(pwd)" || exit
# Unzip the substrate
unzip -q "${substrate_file}" || exit

# Define some variables for clarity
embed_dir="${substrate_dir}/embedded"
embed_bindir="${embed_dir}/bin"
embed_libdir="${embed_dir}/lib"
embed_etcdir="${embed_dir}/etc"
embed_gemsdir="${embed_dir}/gems"

# Set some environment variables so rubygems will
# install into the substrate
export GEM_PATH="${embed_gemsdir}"
export GEM_HOME="${GEM_PATH}"
export GEMRC="${embed_etcdir}/gemrc"

# Update our PATH so we pick up the gem command
# from the substrate
export PATH="${embed_bindir}:${PATH}"

# Before we install the Vagrant gem we need
# to update the rbconfig file so anything that
# needs to be compiled is universal. The changes
# won't matter outside of this operation as they
# are not persisted
rbconf_files=( "${embed_libdir}/ruby/3."*/*-darwin*/rbconfig.rb )

info "Updating rbconfig to build universal extensions..."

# Both configuration files are modified so it
# will not matter what the host architecture
# is that may be running this
for rbconfig_file in "${rbconf_files[@]}"; do
    if [ ! -f "${rbconfig_file}" ]; then
        error "Invalid detected path for rbconfig.rb file (%s)" "${rbconfig_file}"
    fi
    # Create the file to store the updated contents
    rbconfig_file_new="${rbconfig_file}.new"
    touch "${rbconfig_file_new}" || exit

    # Read the file and apply the required updates
    while read -r line; do
        # Ensure both architectures are defined in flags for building
        if [[ "${line}" = *'CONFIG["CFLAGS"]'* ]] ||
               [[ "${line}" = *'CONFIG["CPPFLAGS"]'* ]] ||
               [[ "${line}" = *'CONFIG["CXXFLAGS"]'* ]] ||
               [[ "${line}" = *'CONFIG["LDFLAGS"]'* ]]; then
            # Trim the final quote from the line
            line="${line%\"}"
            # Append architecture flags and closing quote
            line+=" -arch arm64 -arch x86_64\""
        fi
        # Write line to new file
        printf "%s\n" "${line}" >> "${rbconfig_file_new}"
    done < "${rbconfig_file}"
    # Now replace the original
    mv -f "${rbconfig_file_new}" "${rbconfig_file}"
done

# Now we are ready for the gem installation
info "Installing Vagrant RubyGem..."
# Install the vagrant gem. Use the "ruby" platform so
# gems with pre-built extensions are not fetched.

# NOTE: This environment variable was extracted from the
#       grpc gem and prevents it from modifying the arch
#       flags
export RCD_HOST_RUBY_VERSION=1
gem install --platform ruby "${vagrant_gem}" || exit

# Remove intermediate files from grpc extension
rm -rf "${embed_gemsdir}/gems/grpc-"*/src/ruby/ext/grpc/libs
rm -rf "${embed_gemsdir}/gems/grpc-"*/src/ruby/ext/grpc/objs

# Remove cached gems
rm -f "${embed_gemsdir}/cache/"*.gem

# Remove binary from fsevent as it uses an SDK that is unsupported
rm -f "${embed_gemsdir}/gems/rb-fsevent"*/bin/fsevent_watch

# RubyGems 3.4 introduced extension cleanup and should run `make clean`
# after building gem extensions. Ruby 3.1.4 includes RubyGems 3.4.8 but
# artifacts still remain from the build process. They can be located as
# .o files, so scan for them and delete them.
shopt -s globstar || exit
entries=( "${embed_gemsdir}/gems/"**/*.o )
shopt -u globstar || exit
for leftover in "${entries[@]}"; do
    if [ -f "${leftover}" ]; then
        rm -f "${leftover}"
    fi
done

# For gems with extensions, RubyGems determines if the gem
# has been built based on the existence of a file (gem.build_complete).
# This file is located in the embedded gems extension directory
# under a subdirectory including the current platform as identified
# by RubyGems (Gem::Platform.local.to_s). Since we are cross building
# this, both platform directories need to exist so RubyGems will
# accurately detect extensions as properly built on both platforms.
ext_dir="${embed_gemsdir}/extensions"
ext_entries=( "${ext_dir}"/* )
if [ "${#ext_entries[@]}" -ne 1 ]; then
    printf "Expected one entry in extensions but found %d, halting (%s)\n" \
        "${#ext_entries[@]}" "${ext_entries[*]}"
    exit 1
fi

source_entry="${ext_entries[0]}"
if [[ "${source_entry}" = *"${x86_platform}"* ]]; then
    target_platform="${arm64_platform}"
elif [[ "${source_entry}" = *"${arm64_platform}"* ]]; then
    target_platform="${x86_platform}"
else
    printf "Source extension directory does not match expected platforms, halting (%s)\n" \
        "${source_entry}"
    exit 1
fi
target_entry="${ext_dir}/${target_platform}"

mkdir -p "${target_entry}" || exit
cp -r "${source_entry}"/* "${target_entry}"/ || exit

# Now pack up the gem contents to return
output_file="${cwd}/vagrant-package-contents.zip"

info "Packing Vagrant gem installation..."
zip -q -r "${output_file}" "./embedded/gems" || exit

# Hop out of substrate
popd > /dev/null || exit

# Clean up our substrate directory
rm -rf "${substrate_dir}"

# Write artifact path only to stdout
printf "Vagrant gem installation artifact: " >&2
printf "%s" "${output_file}"
