#!/bin/bash -e

# Build and package Docker image of Memgraph.

function print_help () {
    echo "Usage: $0 [--latest] MEMGRAPH_PACKAGE.(deb|rpm)"
    echo "Optional arguments:"
    echo -e "\t-h|--help\t\tPrint help."
    echo -e "\t--latest\t\tTag image as latest version."
    echo -e "\t--keep-image-loaded <true|false>\tKeep built image loaded in local Docker daemon (default false)."
    echo -e "\t--package-flavour <prod|debug|fips>\tImage variant to build (default prod)."
}

working_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

latest_image=""
tag_latest=""
toolchain_version="v8"
package_path=""
arch=""
src_path=""
custom_mirror=false
malloc=false
keep_image_loaded=false
package_flavour="prod"
while [[ $# -gt 0 ]]; do
  case "$1" in
    --latest)
        latest_image="memgraph/memgraph:latest"
        tag_latest="-t memgraph/memgraph:latest"
        shift
    ;;
    --package-path)
        package_path=$2
        shift 2
    ;;
    --src-path)
        src_path=$2
        shift 2
    ;;
    --toolchain)
        toolchain_version=$2
        shift 2
    ;;
    --arch)
        arch=$2
        shift 2
    ;;
    -h|--help)
        print_help
        exit 0
    ;;
    --custom-mirror)
        if [[ "$2" == "true" ]]; then
          custom_mirror=true
        else
          custom_mirror=false
        fi
        shift 2
    ;;
    --malloc)
        malloc=$2
        shift 2
    ;;
    --keep-image-loaded)
        keep_image_loaded=$2
        shift 2
    ;;
    --package-flavour)
        package_flavour=$2
        shift 2
    ;;
    *)
        echo -e "Error: Unknown option '$1'"
        exit 1
    ;;
  esac
done

case "$package_flavour" in
  prod|debug|fips) ;;
  *)
    echo "Error: --package-flavour must be 'prod', 'debug' or 'fips' (got '$package_flavour')"
    exit 1
  ;;
esac
echo "latest_image: $latest_image, latest_tag: $tag_latest, package_path: $package_path, src_path: $src_path, toolchain: $toolchain_version, arch: $arch, custom_mirror: $custom_mirror"
if [[ "$package_path" == "" || ! -f "$package_path" ]]; then
  echo "File '$package_path' does not exist!"
  exit 1
fi

# Map the user-facing --package-flavour onto the dockerfile stage name. All
# flavours share a single dockerfile per (toolchain, extension); 'debug' is a
# thin layer on top of 'prod' adding the memgraph-debuginfo package and debug
# tooling, while 'fips' is a parallel stage.
docker_target="prod"
docker_arg_source_code=""
tag_extension=""
case "$package_flavour" in
  debug)
    if [[ -z "$src_path" ]]; then
      echo "Error: --package-flavour debug requires --src-path"
      exit 1
    fi
    cp -r "$src_path" "${working_dir}/src"
    docker_target="relwithdebinfo"
    docker_arg_source_code="--build-arg SOURCE_CODE=src"
    tag_extension="-relwithdebinfo"
  ;;
  fips)
    docker_target="prod-fips"
    tag_extension="-fips"
  ;;
esac

if [[ "$malloc" == true ]]; then
  tag_extension="${tag_extension}-malloc"
fi

# Copy the .deb to working directory.
cp "$package_path" "${working_dir}/"

extension="${package_path##*.}"

# For the debug flavour, locate the matching memgraph-debuginfo package next
# to the main one and stage it into the build context. The dockerfile mounts
# it into the relwithdebinfo stage and dpkg -i's it on top of the main install.
debuginfo_package_path=""
debuginfo_package_name=""
if [[ "$package_flavour" == "debug" ]]; then
  case "$extension" in
    deb)
      debuginfo_package_path="${package_path/memgraph_/memgraph-debuginfo_}"
      ;;
    rpm)
      debuginfo_package_path="${package_path/memgraph-/memgraph-debuginfo-}"
      ;;
  esac
  if [[ ! -f "$debuginfo_package_path" ]]; then
    echo "Error: expected memgraph-debuginfo package next to '$package_path'"
    echo "       (looked for '$debuginfo_package_path' — produce it via component-aware cpack)"
    exit 1
  fi
  cp "$debuginfo_package_path" "${working_dir}/"
  debuginfo_package_name="$(basename "$debuginfo_package_path" | sed "s/\.${extension}\$//")"
fi

cd ${working_dir}

# copy SSL files to docker build directory
mkdir -p "${working_dir}/openssl"
cp -v ../../build/openssl*.deb "${working_dir}/openssl"
cp -v ../../build/libssl3t64*.deb "${working_dir}/openssl"

if [[ "$package_flavour" == "fips" ]]; then
  if ! ls "${working_dir}"/openssl/openssl-fips-provider*.deb >/dev/null 2>&1; then
    echo "Error: --package-flavour fips needs the openssl-fips-provider package."
    echo "       Re-fetch with: tools/ci/openssl/fetch-openssl-packages.sh --fips --dest-dir build"
    exit 1
  fi
  # The provider Depends on libssl3t64 (= <version>+fips<fips-version>), so a
  # stock libssl3t64 left over from a previous fetch would break the install.
  if ! ls "${working_dir}"/openssl/libssl3t64*+fips*.deb >/dev/null 2>&1; then
    echo "Error: the staged libssl3t64 package is not the FIPS build."
    echo "       Re-fetch with: tools/ci/openssl/fetch-openssl-packages.sh --fips --dest-dir build"
    exit 1
  fi
fi

cp -v ../../src/auth/reference_modules/requirements.txt "${working_dir}/auth-module-requirements.txt"

# stage wheels dir so the python-base stage can install packages that lack
# prebuilt linux wheels on PyPI (e.g. gssapi). It must actually hold the gssapi
# wheels: the dockerfile installs them with --only-binary=gssapi, so an empty
# dir fails the build rather than silently falling back to the PyPI sdist. Fill
# it with:
#   tools/ci/fetch-gssapi-wheels.sh --arch <amd|arm> --dest-dir release/docker/wheels
# which is what the packaging workflow runs before calling this script.
mkdir -p "${working_dir}/wheels"

dockerfile_path="${working_dir}/${toolchain_version}_${extension}.dockerfile"
echo "Dockerfile: $dockerfile_path (target: $docker_target)"
if [[ "$extension" == "deb" ]]; then
  # Extract version and offering from deb name.
  package_name=`echo $(basename "$package_path") | sed 's/.deb$//'`
  version=`echo ${package_name} | cut -d '_' -f 2 | rev | cut -d '-' -f 2- | rev | tr '+~' '__'`
elif [[ "$extension" == "rpm" ]]; then
  # Extract version and offering from rpm name.
  package_name=`echo $(basename "$package_path") | sed 's/.rpm$//'`
  version=`echo ${package_name} | cut -d '-' -f 2 | rev | cut -d '-' -f 2- | rev`
  version=${version%_1}
else
  echo "Invalid file sent as the package"
  print_help
  exit 1
fi

tag="${version}${tag_extension}"
image_name="memgraph/memgraph:${tag}"
image_package_name="memgraph-${tag}-docker.tar.gz"

if [[ "$custom_mirror" == true ]]; then
  cp -v ../../tools/ci/ubuntu-mirrors/${arch}/ci.sources "${working_dir}/ci.sources"
fi

# copy scripts to be bind mounted for setting/unsetting mirrors
../../tools/ci/mirrors/stage.sh "${working_dir}"

# Assemble the optional --build-arg for DEBUGINFO_BINARY_NAME so it only
# appears when the relwithdebinfo target needs it.
debuginfo_build_arg=()
if [[ -n "$debuginfo_package_name" ]]; then
  debuginfo_build_arg+=(--build-arg "DEBUGINFO_BINARY_NAME=${debuginfo_package_name}")
fi

# Build docker image.
# NOTE: --pull is here to always pull that latest base image because of security patches.
docker build --pull -t ${image_name} ${tag_latest} -f ${dockerfile_path} \
--target ${docker_target} \
--build-arg BINARY_NAME=${package_name} \
--build-arg EXTENSION=${extension} ${docker_arg_source_code} \
"${debuginfo_build_arg[@]}" \
--build-arg CUSTOM_MIRROR=$custom_mirror \
--build-arg TARGETARCH="" .
docker save ${image_name} ${latest_image} | gzip > ${image_package_name}
rm "${working_dir}/${package_name}.${extension}"
if [[ -n "$debuginfo_package_name" ]]; then
  rm -f "${working_dir}/${debuginfo_package_name}.${extension}"
fi
if [[ -n "$src_path" ]]; then
  rm -rf "${working_dir}/src"
fi

# NOTE: the image SBOM is generated as an explicit step in the CI workflow
# (stage 2), from the memgraph/infra scripts, after this image is built.
if [[ "$keep_image_loaded" != "true" ]]; then
  docker image rm $image_name || true
fi
echo "Built Docker image at '${working_dir}/${image_package_name}'"
