#!/bin/bash

COLOR_NC='033[0m' # No Color COLOR_GRAY='033[1;30m' COLOR_RED='033[0;31m' COLOR_LCYAN='033[1;36m' COLOR_YELLOW='033[1;33m' COLOR_LGREEN='033[1;32m'

logv() {

[[ -z "$VERBOSE" ]] && return 0;

local msg=$(echo "$@" | sed "s/$CONTENTFUL_MANAGEMENT_TOKEN/\*\*\*\*\*/" )
>&2 echo -e "${COLOR_GRAY}$msg${COLOR_NC}" || true

}

logerr() {

>&2 echo -e "${COLOR_RED}$@${COLOR_NC}"

}

curlv() {

logv "curl" $@
curl "$@"

}

execv() {

logv "$@"
"$@"

}

## *** Argument Parsing & validation ***

usage() {

echo "$0 <command> [opts]
Commands:
  migrate [dir|file]
    runs pending migration files in the given directory
      * [dir|file] optional - Default: db/migrate

  setup [file]
    initializes a space with bare-minimum schema and seeds
      * [file] optional - default: db/contentful-schema.json

  backup [file]
    downloads a backup of the current space to the given file
      * [file] optional - default: timestamped file in current directory

  clean [no-init]
    Deletes all data in a given space and optionally sets it up again
    using 'bin/contentful setup'. 
      * [no-init] optional - Skips the 'setup' step at the end.

  restore [file]
    restores a given backup file into the current space
      * [file] optional - default: the most recent backup file in the current directory

  new_env
    deletes the current working environment if it exists and makes a new clone of 'master'.
      * -e [to environment ID] optional - the current working environment.  Default: \$USER

  generate [name]
    Creates a sample migration in the db/migrate directory
      * [name] optional - default: 'contentful_migration'

Flags:" && \
  grep " .)\ #" $0
echo "
Examples:" && \
  grep -i "#\ example:" $0 | awk '{$1=""; $2=""; print "  "$0}'

}

parse_args() {

OPTIND=1
local s=$(echo "$1" | tr '[:upper:]' '[:lower:]')
case "$s" in
  migrate|setup|backup|export|restore|new_env|import|generate|clean|help|h|\?)
    export subcommand=$s
    OPTIND=2
    ;;
esac

# Parse flags
while getopts ":hyvse:a:" arg; do
  case $arg in
    y) # Yes - skip prompts
      export YES="-y"
      ;;
    s) # Contentful Space ID - overrides env var CONTENTFUL_SPACE_ID
      export CONTENTFUL_SPACE_ID=$OPTARG
      ;;
    a) # Contentful Mgmt Token - overrides env var CONTENTFUL_MANAGEMENT_TOKEN
      export CONTENTFUL_MANAGEMENT_TOKEN=$OPTARG
      ;;
    e) # Contentful environment ID - overrides env var CONTENTFUL_ENVIRONMENT
      export CONTENTFUL_ENVIRONMENT=$OPTARG
      ;;
    v) # Verbose mode - extra output
      export VERBOSE=true
      ;;
    h) # Display help.
      usage
      exit 0
      ;;
    *)
      logerr "Unknown option: '$OPTARG'"
      usage
      exit -1
      ;;
  esac
done

export OPTIND

}

parse_args $@ && shift $(($OPTIND - 1))

# If they put args before the command like 'bin/contentful -s 1xab migrate -y', try parsing again
[ -z “$subcommand” ]

&& parse_args $@ && shift $(($OPTIND - 1))

require_environment() {

[[ -z "$CONTENTFUL_SPACE_ID" ]] && logerr "Please set CONTENTFUL_SPACE_ID environment variable or use '-s' flag." && exit -1;
[[ -z "$CONTENTFUL_MANAGEMENT_TOKEN" ]] && logerr "Please set CONTENTFUL_MANAGEMENT_TOKEN environment variable or use '-a' flag." && exit -1;
if [[ ! -f node_modules/.bin/contentful-migration ]]; then
  command -v npm >/dev/null 2>&1 || (logerr "I require 'npm' but it's not installed.  Please install nodejs."; exit -1)
  execv npm install
  [[ -f node_modules/.bin/contentful-migration ]] || (logerr "Failed installing node modules - please ensure contentful CLI is installed"; exit -1)
fi

}

## *** Utility functions ***

confirm() {

[[ -z "$2" ]] && [[ ! -z "$YES" ]] && logv "$1 (y/n): confirmed by -y flag" && return 0;

while true; do
  if [[ -z "$2" ]]; then
    read -p $'\033[1;36m'"$1"' (y/n): '$'\033[0m' yn
  else
    # double confirm - extra dangerous.
    read -p $'\033[0;31m'"$1"' (y/n): '$'\033[0m' yn
  fi
  case $yn in
      [Yy]* ) return 0;;
      [Nn]* ) return 1;;
      * ) echo "Please answer yes or no.";;
  esac
done

}

get_space_name() {

curlv -s https://api.contentful.com/spaces/$1?access_token=$CONTENTFUL_MANAGEMENT_TOKEN | jq -r .name | tr '[:upper:]' '[:lower:]'

}

# Man I wish I understood sed… stackoverflow.com/a/29060802 # $1 File # $2 Find # $3 Replace / Append replace_append() {

if grep -q "^$2" "$1"
then
    sed -i.bak "s/^$2.*$/$3/" "$1"
else
    echo "\n$3" >> "$1"
fi

}

set -e

# *** Commands ***

# Example: bin/contentful migrate -y -s 1xab -a $MY_TOKEN db/migrate/20180101120000_add_content_type_dog.ts # equivalent to: bin/rake db:migrate migrate() {

ARG="$1"
[[ -z "$ARG" ]] && ARG="db/migrate"
[[ -d "$ARG" ]] && ARG="batch $ARG"

require_environment

[[ ! -z "$CONTENTFUL_ENVIRONMENT" ]] && ENV="--environment-id $CONTENTFUL_ENVIRONMENT"

execv node_modules/.bin/ts-node node_modules/.bin/contentful-migration \
  -s $CONTENTFUL_SPACE_ID $ENV -a $CONTENTFUL_MANAGEMENT_TOKEN \
  $YES -p $ARG

mkdir -p db
execv node_modules/.bin/contentful-export --export-dir db --content-file contentful-schema.json \
  --space-id $CONTENTFUL_SPACE_ID $ENV --management-token $CONTENTFUL_MANAGEMENT_TOKEN \
  --query-entries 'content_type=migrationHistory' \
  --query-assets 'sys.id=false'

if [[ $(git diff-index --name-only HEAD | grep 'db/contentful-schema.json') == "" ]]; then
  echo -e "${COLOR_LGREEN}✓ Schema in contentful space is equivalent to stored schema${COLOR_NC}"
else
  echo -e "${COLOR_YELLOW}⚠️  Schema changed after running migrations${COLOR_NC}"
fi

}

# Example: bin/contentful backup -s 1xab -a $MY_TOKEN 2018_01_01.1xab.dump.json # equivalent to: bin/rake db:dump backup() {

FILE="$1"
[[ ! -z "$FILE" ]] && FILE="--content-file $FILE" && shift

require_environment

[[ ! -z "$CONTENTFUL_ENVIRONMENT" ]] && ENV="--environment-id $CONTENTFUL_ENVIRONMENT"

execv node_modules/.bin/contentful-export $FILE \
  --space-id $CONTENTFUL_SPACE_ID $ENV --management-token $CONTENTFUL_MANAGEMENT_TOKEN \
  $@

}

# Example: bin/contentful restore -y -s 1xab -a $MY_TOKEN 2018_01_01.1xab.dump.json # equivalent to: bin/rake db:restore restore() {

FILE="$1"
if [[ -z "$FILE" ]]; then
  FILE=$(ls contentful-export-$CONTENTFUL_SPACE_ID-* | sort -r | head -n 1)
  [[ -z "$FILE" ]] && logerr "No file given on command line" && exit -1
fi

name=$(get_space_name $CONTENTFUL_SPACE_ID)
if [[ ! -z "$CONTENTFUL_ENVIRONMENT" ]]; then
  ENV="--environment-id $CONTENTFUL_ENVIRONMENT"
  name="$name/$CONTENTFUL_ENVIRONMENT"
fi
confirm "Import $FILE into $name?" || exit -1

require_environment

execv node_modules/.bin/contentful-import \
  --space-id $CONTENTFUL_SPACE_ID $ENV --management-token $CONTENTFUL_MANAGEMENT_TOKEN \
  --content-file $FILE

}

# Example: bin/contentful setup -y -s 1xab -a $MY_TOKEN db/my-schema.json # equivalent to: bin/rake db:setup setup() {

FILE="$1"
[[ -z "$FILE" ]] && FILE=db/contentful-schema.json

name=$(get_space_name $CONTENTFUL_SPACE_ID)
if [[ ! -z "$CONTENTFUL_ENVIRONMENT" ]]; then
  ENV="--environment-id $CONTENTFUL_ENVIRONMENT"
  name="$name/$CONTENTFUL_ENVIRONMENT"
fi

confirm "Initialize space $name from seed file $FILE?" || exit -1

require_environment

execv node_modules/.bin/contentful-import \
  --space-id $CONTENTFUL_SPACE_ID $ENV --management-token $CONTENTFUL_MANAGEMENT_TOKEN \
  --content-file $FILE

migrate

}

# Example: bin/contentful clean -s 1xab -a $MY_TOKEN clean() {

command -v jq >/dev/null 2>&1 || (logerr "I require 'jq' but it's not installed.  Please run 'brew install jq'"; exit -1)

require_environment

[[ -z "$CONTENTFUL_ENVIRONMENT" ]] && CONTENTFUL_ENVIRONMENT="$USER"
[[ "$CONTENTFUL_ENVIRONMENT" == "master" ]] && logerr "cannot delete the master environment" && exit -1

name=$(get_space_name $CONTENTFUL_SPACE_ID)
code=$(curlv -s -o /dev/null -w "%{http_code}" https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/environments/$CONTENTFUL_ENVIRONMENT\?access_token\=$CONTENTFUL_MANAGEMENT_TOKEN)
[[ $code == "404" ]] && logerr "$CONTENTFUL_ENVIRONMENT does not exist in $name" && return 0;

confirm "This will delete the '$CONTENTFUL_ENVIRONMENT' environment from $name.  Are you sure?" || exit -1

local bkup_file="contentful-export-$CONTENTFUL_SPACE_ID-${CONTENTFUL_ENVIRONMENT}-`date +"%Y-%m-%dT%H-%M-%S"`.json"
backup $bkup_file

curlv --fail -XDELETE https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/environments/$CONTENTFUL_ENVIRONMENT\?access_token\=$CONTENTFUL_MANAGEMENT_TOKEN

}

# Example: bin/contentful new_env -e gordon_dev new_env() {

command -v jq >/dev/null 2>&1 || (logerr "I require 'jq' but it's not installed.  Please run 'brew install jq'"; exit -1)

require_environment
name=$(get_space_name $CONTENTFUL_SPACE_ID)
[[ -z "$CONTENTFUL_ENVIRONMENT" ]] && CONTENTFUL_ENVIRONMENT="$USER"
[[ "$CONTENTFUL_ENVIRONMENT" == "master" ]] && logerr "cannot delete the master environment" && exit -1

echo -e "${COLOR_LCYAN}This will delete '$CONTENTFUL_ENVIRONMENT' and recreate it from master.${COLOR_NC}"
confirm "Continue?" || exit -1
export YES='-y' # don't keep bugging the user

clean

# make the environment
resp=$(curlv -s --fail -XPUT https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/environments/$CONTENTFUL_ENVIRONMENT \
  -H "Authorization: Bearer ${CONTENTFUL_MANAGEMENT_TOKEN}" \
  -H "Content-Type: application/vnd.contentful.management.v1+json" \
  -d "{ \"name\": \"${CONTENTFUL_ENVIRONMENT}\" }")

while [ ! $(echo "$resp" | jq -r .sys.status.sys.id) == "ready" ]
do
  logv "waiting for environment $CONTENTFUL_ENVIRONMENT to be ready..."
  sleep 1
  resp=$(curlv -s https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/environments/$CONTENTFUL_ENVIRONMENT \
    -H "Authorization: Bearer ${CONTENTFUL_MANAGEMENT_TOKEN}")
done

logv "get the API keys and update the one matching ours to point to the new environment"
keys=$(curlv -s --fail https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/api_keys\?access_token\=$CONTENTFUL_MANAGEMENT_TOKEN)
my_key=$(echo "$keys" | jq -r ".items[] | select(.accessToken == \"$CONTENTFUL_ACCESS_TOKEN\")")
my_key_id=$(echo "$my_key" | jq -r ".sys.id")
my_key_version=$(echo "$my_key" | jq -r ".sys.version")
new_env_links=$(echo "$my_key" | jq ".environments + [{ \"sys\": { \"id\": \"$CONTENTFUL_ENVIRONMENT\", \"type\": \"Link\", \"linkType\": \"Environment\" } }] | { \"environments\": . }")

curlv -s -o /dev/null --fail -XPUT https://api.contentful.com/spaces/$CONTENTFUL_SPACE_ID/api_keys/$my_key_id \
  -H "Authorization: Bearer ${CONTENTFUL_MANAGEMENT_TOKEN}" \
  -H "Content-Type: application/vnd.contentful.management.v1+json" \
  -H "X-Contentful-Version: ${my_key_version}" \
  -d "${new_env_links}"

execv replace_append .env.local "CONTENTFUL_ENVIRONMENT\=" "CONTENTFUL_ENVIRONMENT=$CONTENTFUL_ENVIRONMENT"
execv replace_append .env.test.local "CONTENTFUL_ENVIRONMENT\=" "CONTENTFUL_ENVIRONMENT=$CONTENTFUL_ENVIRONMENT"

echo "Environment ${CONTENTFUL_ENVIRONMENT} successfully created!"

}

# Example: bin/contentful generate add content type dog # equivalent to: bin/rails generate migration add_content_type_dog generate() {

migration="

import Migration from 'contentful-migration-cli'

export = function (migration: Migration) {

const dog = migration.createContentType('dog', {
  name: 'Dog'
})

const name = dog.createField('name')
name.name('Name')
  .type('Symbol')
  .required(true)

} “

timestamp=$(date +%Y%m%d%H%M%S)
filename="$@"
[[ -z "$filename" ]] && filename="contentful_migration"
filename=${filename// /\_}
filename="db/migrate/${timestamp}_${filename}.ts"
echo "$migration" > $filename
echo "generated file $filename"

}

case $subcommand in

migrate)
  migrate $@
  ;;
backup|export)
  backup $@
  ;;
restore|import)
  restore $@
  ;;
setup)
  setup $@
  ;;
generate)
  generate $@
  ;;
clean)
  clean $@
  ;;
new_env)
  new_env $@
  ;;
help|h|\?)
  usage
  ;;
*)
  logerr "Unknown command: '$1'"
  usage
  exit -1
  ;;

esac