#!/bin/bash
# Bash strict mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail

show_help(){
    echo """
Usage: install_plugin.sh [-d] [-f <plugin folder>]
  -f, --folder <plugin folder>        The folder where the plugin to install is located.
  -g, --git <https git repo url>      An url to a git repo containing the plugin to install.
  -u, --url <plugin url>              An url to a .tar.gz file containing the plugin to install.
      --hash <plugin hash>            If provided the plugin's contents will be hashed and checked against this hash, if they do not match the install will fail.
  -d, --dev                           Install the plugin for development.
  -r, --runtime                       If provided any runtime plugin setup scripts will be run if found. Should never be set if being called from a Dockerfile.
  -o, --overwrite                     If provided any existing plugin of the same name will be overwritten and force re-installed, built and/or setup.
  -h, --help                          Show this help message and exit.

A Baserow plugin is a folder named after the plugin, containing one or both of the
following sub-folders:
  - a 'backend' folder, containing the plugin's backend code. This must be a valid Python
    package.
    - If a 'backend/build.sh' script exists this will be executed to perform any
      additional plugin installation tasks.
  - a 'web-frontend' folder, containing the plugin's backend code. This must be a valid
    node package.
    - If a 'web-frontend/build.sh' script exists this will be executed to perform any
      additional plugin installation tasks.
"""
}

source /baserow/plugins/utils.sh

# First parse the args using getopt
VALID_ARGS=$(getopt -o u:dhf:rg:o --long hash:,url:,git:,help,dev,folder:,runtime,overwrite -- "$@")
if [[ $? -ne 0 ]]; then
    error "Incorrect options provided."
    show_help
    exit 1;
fi
eval set -- "$VALID_ARGS"

if [[ "$*" == "--" ]]; then
    error "No arguments provided."
    show_help
    exit 1;
fi

# Next loop over the user provided args and set flags accordingly.
dev=false
url=
folder=
hash=
git=
exclusive_flag_count=0
runtime=
overwrite=
# shellcheck disable=SC2078
while [ : ]; do
  case "$1" in
    -d | --dev)
        log "Installing plugin in dev mode."
        dev=true
        shift
        ;;
    -f | --folder)
        folder="$2"
        shift 2
        exclusive_flag_count=$((exclusive_flag_count+1))
        ;;
    --hash)
        hash="$2"
        shift 2
        ;;
    -u | --url)
        url="$2"
        shift 2
        exclusive_flag_count=$((exclusive_flag_count+1))
        ;;
    -g | --git)
        git="$2"
        shift 2
        exclusive_flag_count=$((exclusive_flag_count+1))
        ;;
    -r | --runtime)
        runtime="true"
        shift
        ;;
    -o | --overwrite)
        overwrite="true"
        shift
        ;;
   -h | --help)
        show_help
        exit 0;
        ;;
    --)
        shift
        break
        ;;
  esac
done

if [[ "$exclusive_flag_count" -eq "0" ]]; then
    error "You must provide one of the following flags: --folder, --url or --git"
    show_help
    exit 1;
fi

if [[ "$exclusive_flag_count" -gt "1" ]]; then
    echo "You must provide only one of the following flags: --folder, --url or --git"
    show_help
    exit 1;
fi

# --git was provided, download the plugin using git..
if [[ -n "$git" ]]; then
    log "Downloading plugin from git repo at $git."
    temp_work_dir=$(mktemp -d)
    cd "$temp_work_dir"
    git clone "$git" .

    dirs=("$temp_work_dir"/plugins/*/)
    num_dirs=${#dirs[@]}
    if [[ "$num_dirs" -ne 1 ]]; then
        error "$git does not look like a Baserow plugin. The plugins/ subdirectory in the repo must contain exactly one sub-directory."
        exit 1;
    fi
    folder=${dirs[0]}
fi

# --url was set, download the url, untar it to a temp dir, and verify it only has one
# sub dir.
if [[ -n "$url" ]]; then
    log "Downloading and extracting plugin from $url."
    temp_work_dir=$(mktemp -d)
    curl -Ls "$url" | tar xz -C "$temp_work_dir"

    dirs=("$temp_work_dir"/*/plugins/*/)
    num_dirs=${#dirs[@]}
    if [[ "$num_dirs" -ne 1 ]]; then
        error "$url does not look like a Baserow plugin. The plugin archive must contain a plugins/ sub-directory itself containing exactly one sub-directory for the plugin."
        exit 1;
    fi
    folder=${dirs[0]}
fi

# copy the plugin at the folder location into the plugin dir if it has not been already.
plugin_name="$(basename -- "$folder")"
plugin_install_dir="$BASEROW_PLUGIN_DIR/$plugin_name"
if [[ ! "$folder" -ef "$plugin_install_dir" ]]; then
  if [[ ! -d "$plugin_install_dir" || "$overwrite" == "true" ]]; then
    log "Copying plugin $plugin_name into plugins folder at $plugin_install_dir."
    mkdir -p "$BASEROW_PLUGIN_DIR"
    rm -rf "$plugin_install_dir"
    cp -Tr "$folder" "$plugin_install_dir"
  else
    log "Found an existing plugin installed at $plugin_install_dir, not overwriting it
        as the --overwrite flag was not provided to this script."
  fi
  folder="$BASEROW_PLUGIN_DIR/$plugin_name"
fi
chown -R "$DOCKER_USER": "$folder"

# Now we've copied the plugin into the plugin dir we can delete the tmp download dir
# if we used it.
if [[ -n "${temp_work_dir:-}" ]]; then
  rm -rf "$temp_work_dir"
fi

# --hash was set, hash the plugin folder and check it matches.
if [[ -n "$hash" ]]; then
  plugin_hash=$(find "$folder" -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum | cut -d " " -f 1 )
  if [[ "$plugin_hash" != "$hash" ]]; then
    error "Plugin $plugin_name does not match the provided hash. This could mean it has been maliciously modified and it is not safe to install."
    error "The plugins hash was: $plugin_hash"
    error "Instead we expected : $hash"
    exit 1;
  else
    log "Plugin ${plugin_name}'s hash matches provided hash."
  fi
fi

check_and_run_script(){
    if [[ -f "$1/$2" ]]; then
        log "Running ${plugin_name}'s custom $2 script"
        bash "$1/$2"
    fi
}

PLUGIN_BACKEND_FOLDER="$folder/backend"

run_as_docker_user(){
  CURRENT_USER=$(whoami)
  if [[ "$CURRENT_USER" != "$DOCKER_USER" ]]; then
    gosu "$DOCKER_USER" "$@"
  else
    "$@"
  fi
}

# Make sure we create the container markers folder which we will use to check if a
# plugin has been installed or not already inside this container.
mkdir -p /baserow/container_markers

# Install the backend plugin
if [[ -d "/baserow/backend" && -d "$PLUGIN_BACKEND_FOLDER" ]]; then
    log "Found a backend app for ${plugin_name}."
    BACKEND_BUILT_MARKER=/baserow/container_markers/$plugin_name.backend-built
    if [[ ! -f "$BACKEND_BUILT_MARKER" || "$overwrite" == "true" ]]; then
      log "Building ${plugin_name}'s backend app."

      . /baserow/venv/bin/activate
      cd /baserow/backend

      if [[ "$dev" == true ]]; then
          run_as_docker_user pip3 install -e "$PLUGIN_BACKEND_FOLDER"
      else
          run_as_docker_user pip3 install "$PLUGIN_BACKEND_FOLDER"
      fi

      check_and_run_script "$PLUGIN_BACKEND_FOLDER" build.sh
      touch "$BACKEND_BUILT_MARKER"
    else
      log "Skipping install of ${plugin_name}'s backend app as it is already installed."
    fi

    BACKEND_RUNTIME_SETUP_MARKER=/baserow/container_markers/$plugin_name.backend-runtime-setup
    if [[ ( ! -f "$BACKEND_RUNTIME_SETUP_MARKER" || "$overwrite" == "true" ) && $runtime == "true" ]]; then
      check_and_run_script "$PLUGIN_BACKEND_FOLDER" runtime_setup.sh
      touch "$BACKEND_RUNTIME_SETUP_MARKER"
    else
      log "Skipping runtime setup of ${plugin_name}'s backend app."
    fi
fi

# Install the web-frontend plugin
PLUGIN_WEBFRONTEND_FOLDER="$folder/web-frontend"
if [[ -d "/baserow/web-frontend" && -d "$PLUGIN_WEBFRONTEND_FOLDER" ]]; then
    log "Found a web-frontend module for ${plugin_name}."
    WEBFRONTEND_BUILT_MARKER=/baserow/container_markers/$plugin_name.web-frontend-built
    if [[ ! -f "$WEBFRONTEND_BUILT_MARKER" || "$overwrite" == "true" ]]; then
      log "Building ${plugin_name}'s web-frontend module."


      cd /baserow/web-frontend
      run_as_docker_user yarn add "$PLUGIN_WEBFRONTEND_FOLDER" && yarn cache clean

      # We only load web-frontend modules into nuxt which have a built marker. Touch
      # it now so the build-local picks up the newly installed module and builds it.
      touch "$WEBFRONTEND_BUILT_MARKER"
      function finish {
        rm -f "$WEBFRONTEND_BUILT_MARKER"
      }
      trap finish EXIT

      if [[ "$dev" != true ]]; then
        run_as_docker_user /baserow/web-frontend/docker/docker-entrypoint.sh build-local
      else
        log "Installing plugins dev dependencies..."
        # In dev mode yarn install the plugins own dependencies so they are available
        # for linting the plugin etc.
        cd "$PLUGIN_WEBFRONTEND_FOLDER"
        run_as_docker_user yarn install
        cd /baserow/web-frontend
      fi

      check_and_run_script "$PLUGIN_WEBFRONTEND_FOLDER" build.sh
      trap - EXIT
    else
      log "Skipping build of $plugin_name web-frontend module as it has already been built."
    fi

    WEBFRONTEND_RUNTIME_SETUP_MARKER=/baserow/container_markers/$plugin_name.web-frontend-runtime-setup
    if [[ ( -f "$WEBFRONTEND_RUNTIME_SETUP_MARKER" || "$overwrite" == "true" ) && $runtime == "true" ]]; then
      check_and_run_script "$PLUGIN_WEBFRONTEND_FOLDER" runtime_setup.sh
      touch "$WEBFRONTEND_RUNTIME_SETUP_MARKER"
    else
      log "Skipping runtime setup of ${plugin_name}'s web-frontend module."
    fi
fi

log "Fixing ownership of plugins from $(id -u) to $DOCKER_USER in $BASEROW_PLUGIN_DIR"
chown -R "$DOCKER_USER": "$BASEROW_PLUGIN_DIR"
chown -R "$DOCKER_USER": /baserow/container_markers/
log_success "Finished setting up ${plugin_name} successfully."