#! /bin/sh

set -e

print_help() {
cat <<-END
  Usage: $0 <command> [subcommand] [option(s)]

  Commands:
    build                                 Build the project
    help                                  Show this help screen and exit
    install                               Perform installation steps
    process-blocks                        Process blocks
    run-tests                             Runs tests
    serve                                 Start server
    install-app                           Installs Balance Tracker in HAF

  Install subcommands:
    jmeter                                Installs Apache Jmeter
    postgrest                             Installs PostgREST
    backend-runtime-dependencies          Installs backend runtime dependencies
    frontend-runtime-dependencies         Installs frontend runtime dependencies
    test-dependencies                     Installs test dependencies

  Serve subcommands:
    frontend                              Start frontend server
    postgrest-backend                     Start PostgREST backend server
    test-results                          Serve test results generated by run-tests command

  Balance Tracker installation (install-app) options:
    --postgres-host=HOSTNAME              PostgreSQL hostname (default: localhost)
    --postgres-port=PORT                  PostgreSQL port (default: 5432)
    --postgres-user=USERNAME              PostgreSQL user name (default: haf_admin)
    --postgres-url=URL                    PostgreSQL URL (if set, overrides three previous options, empty by default)
    --no-context=true|false               When set to true, do not create context (default: false)
    --no-context                          The same as '--no-context=true'

  Block processing (process-blocks) options:
    --number-of-blocks=INTEGER            Number of blocks to process (default: 10^9), if set to value greater than number of blocks in the database (or 0),
                                            indexer will wait for new blocks
    --postgres-host=HOSTNAME              PostgreSQL hostname (default: localhost)
    --postgres-port=PORT                  PostgreSQL port (default: 5432)
    --postgres-user=USERNAME              PostgreSQL user name (default: btracker_owner)
    --postgres-url=URL                    PostgreSQL URL (if set, overrides three previous options, empty by default)
    --log-dir=DIR-PATH                    Directory for block procesing logs (default: unset),
                                            setting this parameter will cause the block processing to run in the background,
                                            logs will be rotated once they reach 5M size

  Test running options:
    --test-report-dir=PATH                Directory where HTML test report will be generated
    --test-result-path=PATH               File where JTL test result will be generated
    --test-thread-count=NUMBER            Number of threads to be used to run tests (default: 8)
    --test-loop-count=NUMBER              Number of loops to be run during tests (default: 60)
    --backend-port=PORT                   Port used by the backend (default: 3000)
    --backend-host=HOSTNAME               Hostname of backend's host (default: localhost)

  Serving options:
    --frontend-port=PORT                  Frontend port (default: 4000)
    --log-dir=PATH                        Log directory for frontend and/or backend logs
    --postgres-host=HOSTNAME              PostgreSQL hostname (default: localhost)
    --postgres-port=PORT                  PostgreSQL port (default: 5432)
    --postgres-user=USERNAME              PostgreSQL user name (default: btracker_user)
    --postgres-url=URL                    PostgreSQL URL (if set, overrides three previous options, empty by default)
    --postgrest-host=HOST                 PostgREST bind address (default: !4)
                                          See https://postgrest.org/en/stable/references/configuration.html#server-host for
                                          possible values
    --postgrest-port=PORT                 PostgREST bind port (default: 3000)
    --postgrest-admin-server-port=PORT    PostgREST admin port (default: unset, health checks not available)
                                          See https://postgrest.org/en/stable/references/admin.html#health-check for details
    --test-server-port=PORT               Port on which the test report is served
    --test-report-dir=PATH                Directory where HTML test report is located

END
}

build() {
  subcommand=$1
  shift

  case "$subcommand" in
    frontend)
      echo "Building frontend"
      cd gui
      export NODE_ENV=production
      npm install
      npm run build
      npx react-inject-env set
      cd ..
      echo "Finished building frontend"
      ;;
    *)
      echo "Unknown subcommand: $subcommand"
      print_help
      exit 1
  esac
}

install_jmeter() {
  version="5.6.2"
  bin_path="/usr/local/bin/jmeter"
  src_path="/usr/local/src/jmeter-$version"
  echo "Installing Jmeter $version to $bin_path..."

  wget "https://downloads.apache.org/jmeter/binaries/apache-jmeter-${version}.zip" -O jmeter.zip

  unzip "jmeter.zip"
  rm "jmeter.zip"
  sudo mv "apache-jmeter-${version}" "$src_path"

  jmeter="$bin_path-$version"

cat <<-_jmeter | sudo tee "$jmeter"
#!/usr/bin/env bash

cd "$src_path/bin"
./jmeter.sh "\$@"
_jmeter

  sudo chmod +x "$jmeter"
  sudo ln -sf "$jmeter" "$bin_path"

  echo "Finished installing Jmeter"
}

install_postgrest() {
  version="v11.1.0"
  path="/usr/local/bin/postgrest"
  echo "Installing PostgREST $version to $path..."

  wget https://github.com/PostgREST/postgrest/releases/download/$version/postgrest-$version-linux-static-x64.tar.xz -O postgrest.tar.xz

  tar -xJf postgrest.tar.xz

  sudo mv postgrest "$path"

  rm postgrest.tar.xz
  echo "Finished installing PostgrREST"
}

install_backend_runtime_dependencies() {
  echo "Installing backend runtime dependencies..."
  sudo apt-get update
  sudo apt-get -y install \
    apache2-utils \
    curl \
    postgresql-client \
    wget \
    xz-utils
  echo "Finished installing backend runtime dependencies"
}

install_frontend_runtime_dependencies() {
  echo "Installing frontend runtime dependencies..."
  sudo apt-get update
  sudo apt-get -y install curl
  curl https://get.volta.sh | bash
  export VOLTA_HOME="$HOME/.volta"
  export PATH="$VOLTA_HOME/bin:$PATH"
  cd gui
  npm install
  cd ..
  echo "Finished installing frontend runtime dependencies"
}

install_test_dependencies() {
  echo "Installing test dependencies..."
  sudo apt-get update
  sudo apt-get -y install \
    openjdk-8-jdk-headless \
    python3 \
    unzip
  echo "Finished installing test dependencies"
}

install() {
  subcommand=$1
  shift

  case "$subcommand" in
    jmeter)
      install_jmeter
      ;;
    postgrest)
      install_postgrest
      ;;
    backend-runtime-dependencies)
      install_backend_runtime_dependencies
      ;;
    frontend-runtime-dependencies)
      install_frontend_runtime_dependencies
      ;;
    test-dependencies)
      install_test_dependencies
      ;;
    *)
      echo "Unknown subcommand: $subcommand"
      print_help
      exit 1
  esac
}

process_blocks() {
  echo "Running indexer..."
  echo "Arguments: $*"

  block_number=${BLOCK_NUMBER:-null}
  postgres_user=${POSTGRES_USER:-"btracker_owner"}
  postgres_host=${POSTGRES_HOST:-"localhost"}
  postgres_port=${POSTGRES_PORT:-5432}
  postgres_url=${POSTGRES_URL:-""}
  btracker_schema=${BTRACKER_SCHEMA:-"btracker_app"}
  log_dir=${LOG_DIR:-}

  while [ $# -gt 0 ]; do
    case "$1" in
      --number-of-blocks=*)
        block_number="${1#*=}"
        ;;
      --postgres-host=*)
        postgres_host="${1#*=}"
        ;;
      --postgres-port=*)
        postgres_port="${1#*=}"
        ;;
      --postgres-user=*)
        postgres_user="${1#*=}"
        ;;
      --postgres-url=*)
        postgres_url="${1#*=}"
        ;;
      --schema=*)
        btracker_schema="${1#*=}"
        ;;
      --log-dir=*)
        log_dir="${1#*=}"
        ;;
      -*)
          echo "Unknown option: $1"
          print_help
          exit 1
          ;;
      *)
          echo "Unknown argument: $1"
          print_help
          exit 2
          ;;
    esac
    shift
  done

  if [ -z "$block_number" ]; then
    block_number=1000000000

    echo 'Running indexer for existing blocks and expecting new blocks...'
  fi

  postgres_access=${postgres_url:-"postgresql://$postgres_user@$postgres_host:$postgres_port/haf_block_log?application_name=btracker_block_processing"}

  if [ -z "$log_dir" ]; then
    echo "Running indexer in the foreground"
    psql -a -v "ON_ERROR_STOP=1" "$postgres_access" -c "SET SEARCH_PATH TO ${btracker_schema};" -c "call ${btracker_schema}.main('${btracker_schema}', $block_number);"
    echo "Finished running indexer"
  else
    echo "Running indexer in the background"
    mkdir -p "$log_dir"
    psql -a -v "ON_ERROR_STOP=1" "$postgres_access" -c "SET SEARCH_PATH TO ${btracker_schema};" -c "call ${btracker_schema}.main('${btracker_schema}', $block_number);" 2>&1 | /usr/bin/rotatelogs "$log_dir/process-blocks.%Y-%m-%d-%H_%M_%S.log" 5M &
  fi
}

run_tests() {
  test_scenario_path="$(pwd)/tests/performance/test_scenarios.jmx"
  test_result_path=${TEST_RESULT_PATH:-"$(pwd)/tests/performance/result.jtl"}
  test_report_dir=${TEST_REPORT_DIR:-"$(pwd)/tests/performance/result_report"}
  test_thread_count=${TEST_THREAD_COUNT:-8}
  test_loop_count=${TEST_LOOP_COUNT:-60}
  backend_port=${BACKEND_PORT:-3000}
  backend_host=${BACKEND_HOST:-localhost}
  postgres_user=${POSTGRES_USER:-"btracker_owner"}
  postgres_host=${POSTGRES_HOST:-"localhost"}
  postgres_port=${POSTGRES_PORT:-5432}
  postgres_url=${POSTGRES_URL:-""}
  btracker_schema=${BTRACKER_SCHEMA:-"btracker_app"}

  while [ $# -gt 0 ]; do
    case "$1" in
      --test-report-dir=*)
        test_report_dir="${1#*=}"
        ;;
      --test-result-path=*)
        test_result_path="${1#*=}"
        ;;
      --test-thread-count=*)
        test_thread_count="${1#*=}"
        ;;
      --test-loop-count=*)
        test_loop_count="${1#*=}"
        ;;
      --postgres-host=*)
        postgres_host="${1#*=}"
        ;;
      --postgres-port=*)
        postgres_port="${1#*=}"
        ;;
      --postgres-user=*)
        postgres_user="${1#*=}"
        ;;
      --postgres-url=*)
        postgres_url="${1#*=}"
        ;;
      --schema=*)
        btracker_schema="${1#*=}"
        ;;
      --backend-port=*)
        backend_port="${1#*=}"
        ;;
      --backend-host=*)
        backend_host="${1#*=}"
        ;;
      -*)
          echo "Unknown option: $1"
          print_help
          exit 1
          ;;
      *)
          echo "Unknown argument: $1"
          print_help
          exit 2
          ;;
    esac
    shift
  done

  postgres_access=${postgres_url:-"postgresql://$postgres_user@$postgres_host:$postgres_port/haf_block_log"}

  psql -a -v "ON_ERROR_STOP=1" "$postgres_access" -c "SET SEARCH_PATH TO ${btracker_schema};" -c "SELECT ${btracker_schema}.create_btracker_indexes();"

  test_summary_report_path="${test_result_path%jtl}xml"

  rm -f "$test_result_path"
  mkdir -p "${test_result_path%/*}"
  rm -rf "$test_report_dir"
  mkdir -p "$test_report_dir"
  jmeter --nongui --testfile "$test_scenario_path" --logfile "$test_result_path" \
    --reportatendofloadtests --reportoutputfolder "$test_report_dir" \
    --jmeterproperty backend.port="$backend_port" --jmeterproperty backend.host="$backend_host" \
    --jmeterproperty thread.count="$test_thread_count" --jmeterproperty loop.count="$test_loop_count" \
    --jmeterproperty summary.report.path="$test_summary_report_path"
}

start_frontend() {
  echo "Starting frontend..."
  echo "Arguments: $*"

  frontend_port=${FRONTEND_PORT:-}
  log_dir=${LOG_DIR:-}

  while [ $# -gt 0 ]; do
    case "$1" in
      --frontend-port=*)
        frontend_port="${1#*=}"
        ;;
      --log-dir=*)
        log_dir="${1#*=}"
        ;;
      -*)
          echo "Unknown option: $1"
          print_help
          exit 1
          ;;
      *)
          echo "Unknown argument: $1"
          print_help
          exit 2
          ;;
    esac
    shift
  done

  [ -n "$frontend_port" ] && export PORT="$frontend_port"

  if [ -z "$log_dir" ]; then
    echo "Running frontend in the foreground"
    cd gui
    npm run start
    cd ..
    echo "Finished running frontend"
  else
    echo "Running frontend in the background"
    mkdir -p "$log_dir"
    cd gui
    npm run start 2>&1 | /usr/bin/rotatelogs "$log_dir/frontend.%Y-%m-%d-%H_%M_%S.log" 5M &
    cd ..
  fi
}

start_postgrest() {
  echo "Starting PostgREST..."
  echo "Arguments: $*"

  postgres_user=${POSTGRES_USER:-"btracker_user"}
  postgres_host=${POSTGRES_HOST:-"localhost"}
  postgres_port=${POSTGRES_PORT:-5432}
  postgres_url=${POSTGRES_URL:-""}
  PGRST_SERVER_HOST=${PGRST_SERVER_HOST:-"!4"}
  PGRST_SERVER_PORT=${PGRST_SERVER_PORT:-3000}
  PGRST_ADMIN_SERVER_PORT=${PGRST_ADMIN_SERVER_PORT:-}
  log_dir=${LOG_DIR:-}

  while [ $# -gt 0 ]; do
    case "$1" in
      --postgres-host=*)
        postgres_host="${1#*=}"
        ;;
      --postgres-port=*)
        postgres_port="${1#*=}"
        ;;
      --postgres-user=*)
        postgres_user="${1#*=}"
        ;;
      --postgres-url=*)
        postgres_url="${1#*=}"
        ;;
      --postgrest-host=*)
        PGRST_SERVER_HOST="${1#*=}"
        ;;
      --postgrest-port=*)
        PGRST_SERVER_PORT="${1#*=}"
        ;;
      --postgrest-admin-server-port=*)
        PGRST_ADMIN_SERVER_PORT="${1#*=}"
        ;;
      --log-dir=*)
        log_dir="${1#*=}"
        ;;
      -*)
          echo "Unknown option: $1"
          print_help
          exit 1
          ;;
      *)
          echo "Unknown argument: $1"
          print_help
          exit 2
          ;;
    esac
    shift
  done

  export PGRST_DB_URI="${postgres_url:-postgresql://$postgres_user@$postgres_host:$postgres_port/haf_block_log}"
  export PGRST_SERVER_HOST
  export PGRST_SERVER_PORT
  export PGRST_ADMIN_SERVER_PORT

  if [ -z "$log_dir" ]; then
    echo "Running PostgREST in the foreground"
    postgrest postgrest.conf
    echo "Finished running PostgREST"
  else
    echo "Running PostgREST in the background"
    mkdir -p "$log_dir"
    postgrest postgrest.conf 2>&1 | /usr/bin/rotatelogs "$log_dir/balance-tracker.%Y-%m-%d-%H_%M_%S.log" 5M &
  fi
}

serve_test_results() {
  echo "Starting test result server..."
  echo "Arguments: $*"

  port=${TEST_SERVER_PORT:-8000}
  test_report_dir=${TEST_REPORT_DIR:-"$(pwd)/tests/performance/result_report"}

  while [ $# -gt 0 ]; do
    case "$1" in
      --test-report-dir=*)
        test_report_dir="${1#*=}"
        ;;
      --test-server-port=*)
        port="${1#*=}"
        ;;
      -*)
          echo "Unknown option: $1"
          print_help
          exit 1
          ;;
      *)
          echo "Unknown argument: $1"
          print_help
          exit 2
          ;;
    esac
    shift
  done

  python3 -m http.server --directory "$test_report_dir" "$port"
}

serve() {
  echo "Starting server..."
  echo "Arguments: $*"

  subcommand=$1
  shift

  case "$subcommand" in
    frontend)
      start_frontend "$@"
      ;;
    postgrest-backend)
      start_postgrest "$@"
      ;;
    test-results)
      serve_test_results "$@"
      ;;
    *)
      echo "Unknown subcommand: $subcommand"
      print_help
      exit 1
  esac

  echo "Done"
}

install_app() {
  echo "Installing Balance Tracker in HAF..."
  echo "Arguments: $*"
  ./scripts/install_app.sh "$@"
  echo "Finished installing Balance Tracker in HAF"
}

command=$1
shift

case "$command" in
  build)
    build "$@"
    ;;
  install)
    install "$@"
    ;;
  process-blocks)
    process_blocks "$@"
    ;;
  run-tests)
    run_tests "$@"
    ;;
  serve)
    serve "$@"
    ;;
  install-app)
    install_app "$@"
    ;;
  help | --help | -?)
    print_help
    exit 0
    ;;
  *)
    echo "Unknown command: $command"
    print_help
    exit 1
    ;;
esac
