#!/bin/sh

. $(dirname "$0")/utils.sh

#######################################################################
# Ubicloud 
#######################################################################
g_ubicloud_url_root="https://storage-emea-west-pdc-z01.cloud.ubisoft.com:443/v1/AUTH_0f6b56c7c5e14e00b1c069a93207a186"
g_ubicloud_backet_root="root"
g_ubicloud_upload_attempts=0
g_ubicloud_upload_successes=0
g_ubicloud_upload_failures=0
g_ubicloud_upload_matches=0
g_ubicloud_delete_attempts=0
g_ubicloud_delete_successes=0
g_ubicloud_delete_failures=0
g_ubicloud_files_uploaded=""
g_ubicloud_files_already_uploaded=""
g_ubicloud_directories_deleted=""
g_ubicloud_errors=""

# Retrieve Ubisoft Cloud token
function ubicloud_retrieve_token
{
  local username=$1
  local password=$2

  local token_header="x-subject-token:"

  export OS_PROJECT_DOMAIN_NAME=default
  export OS_USER_DOMAIN_NAME=default
  export OS_PROJECT_NAME=GNS-External-CDN
  export OS_USERNAME=$username
  export OS_PASSWORD=$password
  export OS_AUTH_URL=https://api-emea-west-pdc-z01.cloud.ubisoft.com:5000/v3

  TMPFILE=`mktemp`
  chmod 600 ${TMPFILE}

  JSONFILE=`mktemp`
  chmod 600 ${JSONFILE}

  cat >${JSONFILE} <<EOF
  {
    "auth": {
      "identity": {
         "methods": ["password"],
            "password": {
               "user": {
                  "domain": {"name": "${OS_USER_DOMAIN_NAME}"},
                     "name": "${OS_USERNAME}",
                     "password": "${OS_PASSWORD}"
               }
            }
         },
         "scope": {
            "project": {
               "domain": {"name": "${OS_PROJECT_DOMAIN_NAME}"},
                  "name": "${OS_PROJECT_NAME}"
            }
         }
     }
  }
EOF

  curl -si  \
  -H "Content-Type: application/json" \
  -o ${TMPFILE} \
  -d @${JSONFILE} \
  ${OS_AUTH_URL}/auth/tokens #2>/dev/null

  # Retrieve token section
  local raw_token=$(cat ${TMPFILE} | grep "${token_header}")

  # Delete token_header
  raw_token=$(echo "$raw_token" | sed "s/${token_header} //g")

  # Delete '\r' (carriage return) character at the end
  g_token="${raw_token%%[[:cntrl:]]}"

  #echo
  #tail -1 ${TMPFILE} | json_pp

  rm -f ${TMPFILE} ${JSONFILE}
}

# Return the list of files/directory in Ubisoft Cloud
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to retrieve the information from
# $3: Path in Ubisoft Cloud to search
# $4: Whether or not we should delimeter the search. Pass in true if you don't want to recursively search for the path passed in $2
function ubicloud_ls
{
  local token=$1
  local bucket_id=$2
  local path_to_search=$3
  local use_delimeter=$4
  
  local result=""

  # Compose the url
  local url="${g_ubicloud_url_root}"
  if [[ "$bucket_id" != "" ]]; then
    url+="/${bucket_id}"
  fi

  if [[ "$path_to_search" != "" ]]; then
    url+="/?prefix=${path_to_search}"
  fi

  if [[ "$use_delimeter" = true ]]; then
    if [[ "$url" != *"/" ]]; then
      url+="/"
    fi
    url+="&delimiter=/"
  fi

  local list=$(curl -X GET -i -H "X-Auth-Token: $token" ${url})

  if [[ "$list" == *"200 OK"* ]]; then
    # Filter out the information that is not related to the files contained in 'path_to_search' returned by the command
    for i in $list; do
      if [[ "$i" == "$path_to_search"* ]]; then
        result=$(list_add_item "${result}" "${i}")
      fi
    done
  else
    ubicloud_add_error "$list"
  fi

  echo "$result"
}

# Return a comprehensive list of files/directory in Ubisoft Cloud
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to retrieve the information from
# $3: Path in Ubisoft Cloud to search
function ubicloud_ls_recursive
{
  local token=$1
  local bucket_id=$2
  local path_to_search=$3

  local list=$(ubicloud_ls $token $bucket_id $path_to_search true)
  local result=""

  # Filter out the information that is not related to the files contained in 'path_to_search' returned by the command
  for i in $list; do
    # 'ubicloud_ls' can return 'path_to_search' so we don't have to process it again
    if [[ "${i}" != "${path_to_search}" ]]; then
      local last_token=$(basename -- "$i")
      local directory_inner=""
      if [[ $last_token =~ [0-9]+\.[0-9]+ ]]; then
        directories_inner=$(dirname $i)
      else
        directories_inner=$(ubicloud_ls_recursive $token $bucket_id "$i")
      fi

      if [[ $directories_inner != "" ]]; then
        if [[ ${#result} == 0 ]]; then
          result="$directories_inner"
        elif [[ "$result" != *"$directories_inner"* ]]; then
          result+="\n${directories_inner}"
        fi
      fi
    fi
  done

  echo "$result"
}

# Upload a file to Ubisoft Cloud
# $1: Ubisoft Cloud token
# $2: Source path of the file to upload to Ubisoft Cloud
# $3: Url suffix of the destination path: <bucket_id>/<branch_name>/<version.major.version.minor>/<platform>. Source filename is used for destination filename.
function ubicloud_upload_file
{
  local token=$1
  local src_path=$2
  local url_suffix=$3

  # Uncomment to test errors when uploading
  #if [[ "$g_ubicloud_upload_attempts" -gt 0 ]]; then
  #  token="token_to_fail"
  #fi

  local filename=$(basename "$src_path")
  local dst_path="${g_ubicloud_url_root}/${url_suffix}/${filename}"
  local output_code=$( curl -i -T "$src_path" -X PUT -H "X-Auth-Token: $token" "$dst_path" )

  g_ubicloud_upload_attempts=$((g_ubicloud_upload_attempts+1))

  if [[ "$output_code" == *"Created"* ]]; then
    g_ubicloud_upload_successes=$((g_ubicloud_upload_successes+1))
    g_ubicloud_files_uploaded=$(list_add_item $dst_path "$g_ubicloud_files_uploaded")
  else
    g_ubicloud_upload_failures=$((g_ubicloud_upload_failures+1))
    ubicloud_add_error "${url_suffix}/${filename}: ${output_code}"
  fi
}

# Upload a source directory to Ubisoft Cloud given a source path and a url suffix to add to Ubisoft Cloud url root
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete the file from
# $3: Source path of the directory to upload to Ubisoft Cloud
# $4: Url suffix of the destination path: <bucket_id>/<branch_name>/<version.major.version.minor>/<platform>. Source filename is used for destination filename.
function ubicloud_upload_directory
{
  local token=$1
  local bucket_id=$2
  local path=$3
  local url_suffix=$4
  
  # Make sure source directory exists
  if [ -d $path ]; then
    # Make sure url_suffix is well-formed (no '//'' together which ussually means that some token was empty)
    if [[ "$url_suffix" == "" ]]; then
      ubicloud_add_error "No asset bundles have been uploaded because the app is not configured to use Ubisoft Cloud probably because it is configured to use Store CDN instead."
    elif [[ "$url_suffix" == *"//"* ]]; then
      ubicloud_add_error "No asset bundles have been uploaded because url suffix ${url_suffix} is malformed."
    else
      local files_amount=$(get_total_files $path)
      if [[ $files_amount -gt 0 ]]; then
        local files_in_bucket=$(ubicloud_ls $token $bucket_id $url_suffix false)
        for entry in "$path"/*; do
          local filename=$(basename $entry)
          local dst_path="${url_suffix}/${filename}"
          local found=$(list_contains_item "$files_in_bucket" "${dst_path}")
          if [[ "$found" = true ]]; then
            g_ubicloud_upload_matches=$((g_ubicloud_upload_matches+1))
            g_ubicloud_files_already_uploaded=$(list_add_item "$g_ubicloud_files_already_uploaded" "$dst_path")
          else
            ubicloud_upload_file $token $entry "${bucket_id}/${url_suffix}"
          fi
        done
      fi
    fi
  fi
}

# Upload a source directory to Ubisoft Cloud from teh collection of settings that Hotel PackageManager generated in the workspace
# $1: Ubisoft Cloud token
# $2: Platform which files need to be uploaded to Ubisoft Cloud
function ubicloud_upload_platform_from_settings
{
  local token=$1
  local platform=$2

  local path="Builds/${platform}/settings"
  local bucket_id="$(cat ${path}/remote_host_bucket_id.txt)"
  local src_directory="$(cat ${path}/remote_host_build_path.txt)"
  local url_suffix="$(cat ${path}/remote_host_url_suffix.txt)"
  
  ubicloud_upload_directory $token $bucket_id $src_directory $url_suffix
}

# Delete a file from Ubisoft Cloud
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete the file from
# $3: Relative path (inside this bucket) to the file to delete from Ubisoft Cloud. Do NOT include Ubisoft Cloud url root or bucket id
function ubicloud_delete_file
{
  local token=$1
  
  # Uncomment to test errors when deleting
  #if [[ "$g_ubicloud_delete_attempts" -gt 0 ]]; then
  #  token="token_to_fail"
  #fi

  local dst_bucket_id=$2 
  local relative_path=$3
  local path="${g_ubicloud_url_root}/${dst_bucket_id}/${relative_path}"

  g_ubicloud_delete_attempts=$((g_ubicloud_delete_attempts+1))

  local output_code=$( curl -i $path -X DELETE -H "X-Auth-Token: $token" )
  if [[ "$output_code" == *"No Content"* ]]; then
    g_ubicloud_delete_successes=$((g_ubicloud_delete_successes+1))
  else
    g_ubicloud_delete_failures=$((g_ubicloud_delete_failures+1))
    ubicloud_add_error "${relative_path}: ${output_code}"
  fi
}

# Delete a directory from Ubisoft Cloud
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete the file from
# $3: Relative path (inside this bucket) to the directory to delete from Ubisoft Cloud. Do NOT include Ubisoft Cloud url root or bucket id
# $4: Reason why the directory is requested to be deleted
function ubicloud_delete_directory
{
  local token=$1
  local bucket_id=$2
  local path_to_search=$3
  local reason=$4

  local back_failures=$g_ubicloud_delete_failures
  local entries=$(ubicloud_ls $token $bucket_id $path_to_search false)
  for i in $entries; do
      ubicloud_delete_file $token $bucket_id $i
  done

  # If all files were deleted with no errors then we mark the whole directory as deleted
  if [[ $back_failures -eq $g_ubicloud_delete_failures ]]; then
    g_ubicloud_directories_deleted=$(list_add_item "$g_ubicloud_directories_deleted" "${path_to_search}: $reason")
  fi
}

# Delete all directories in the bucket which names match versions that are lower to the minimum version supported that is passed in as an argument
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete directories from
# $3: Directory to search for versions to delete
# $4: Minimum version (major.minor) to support. Example: 1.2 which means that 0.*, 1.0 and 1.1 will be deleted
function ubicloud_delete_versions_helper
{
  local token=$1
  local bucket_id=$2
  local branch_name=$3
  local min_version=$4

  # Retrieve only the list of directories inside 'branch_name' directory in 'bucket_id' bucket which names should be game versions. 
  local entries=$(ubicloud_ls $token $bucket_id $branch_name true)
  for i in $entries; do
    local version=$(basename $i)

    # Make sure directory name is a version number
    local proceed=$(is_a_version_number $version)
    if [[ "$proceed" = true ]]; then
      # Only delete a version if it's smaller than 'min_version'
      local comp=$(compare_versions $version $min_version) 
      if [[ $comp -lt 0 ]]; then
        ubicloud_delete_directory $token $bucket_id $i "Discontinued version (< ${min_version})"
      fi
    fi
  done
}

# Delete files corresponding to a particular game version from a Ubisoft Cloud bucket
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete files from
# $3: Minimum version (major.minor) to support. Example: 1.2 which means that 0.*, 1.0 and 1.1 will be deleted
function ubicloud_delete_versions
{
  local token=$1
  local bucket_id=$2
  local min_version=$3

  local directories=$(ubicloud_ls_recursive $token $bucket_id "$g_ubicloud_backet_root")
  echo $directories
  for i in $directories; do
    ubicloud_delete_versions_helper $token $bucket_id $i $min_version
  done
}

# Delete those directories inside 'root' directory which names don't match any git branches. Use this method to get rid of files that were uploaded for those branches
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete files from
function ubicloud_delete_stale_branches
{
  local token=$1
  local bucket_id=$2

  local ubicloud_branches=$(ubicloud_ls_recursive $token $bucket_id "$g_ubicloud_backet_root")
  local origin_header="origin"
  local git_branches=$(git branch -r)
  local branches_to_keep=""

  # Loop through all git branches to store the ones to keep
  for i in $git_branches; do
    local branch_name=$(echo "${i/#$origin_header/$g_ubicloud_backet_root}")
    local found=$(list_contains_item "${ubicloud_branches}" "${branch_name}")
    if [[ "$found" = true ]]; then
      branches_to_keep=$(list_add_item "${branches_to_keep}" "${branch_name}")
    fi
  done

  # Delete the directories corresponding to the branches that are not meant to be kept
  for i in $ubicloud_branches; do
    local found=$(list_contains_item "${branches_to_keep}" "${i}")
    if [[ "$found" = false ]]; then
      ubicloud_delete_directory $token $bucket_id $i "Stale branch"
    fi
  done
}
# Clean up files that are not needed anymore
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to delete files from
# $3: Minimum version (major.minor) to support. Example: 1.2 which means that 0.*, 1.0 and 1.1 will be deleted
function ubicloud_clean_up
{
  local token=$1
  local bucket_id=$2
  local min_version=$3

  # Delete stale branches
  ubicloud_delete_stale_branches $token $bucket_id

  # Delete discontinued versions
  ubicloud_delete_versions $token $bucket_id $min_version
}

# Add an error to the output
# $1: Message to add to the error list
function ubicloud_add_error
{
  local error=$1
  g_ubicloud_errors=$(list_add_item "$g_ubicloud_errors" "$error")
}

function ubicloud_print_result
{
  local print_upload=$1
  local print_delete=$2

  echo
  echo
  echo "------------------------------------------------------------------ "
  echo "Ubisoft Cloud output:"
  echo "------------------------------------------------------------------ "
  echo "*)Ubisoft Cloud Errors: "
  list_print "${g_ubicloud_errors}" 

  if [[ "$print_upload" = true ]]; then
    echo
    echo "*)Files uploaded to Ubisoft Cloud:" 
    list_print "${g_ubicloud_files_uploaded}"

    echo
    echo "*)Files not uploaded because they were already in Ubisoft Cloud:" 
    list_print "${g_ubicloud_files_already_uploaded}"

    echo
    echo "*)Summary: ${g_ubicloud_upload_successes}/${g_ubicloud_upload_attempts} files uploaded successfully. ${g_ubicloud_upload_matches} files haven't been uploaded because they were already in the bucket."
  fi

  if [[ "$print_delete" = true ]]; then
    echo
    echo "*)Directories deleted from Ubisoft Cloud:"
    list_print "${g_ubicloud_directories_deleted}"
    echo
    echo "*)Summary: ${g_ubicloud_delete_successes}/${g_ubicloud_delete_attempts} files deleted successfully "
  fi
}

#######################################################################
# Test: This region contains some code that is meant to be used only for testing purposes 
#######################################################################

# Populate a Ubisoft Cloud bucket
# $1: Ubisoft Cloud token
# $2: Bucket id in Ubisoft Cloud to populate
function test_populate_bucket
{
  local token=$1
  local bucket_id=$2
  test_popuplate_bucket_helper $token $bucket_id "test_a"
  test_popuplate_bucket_helper $token $bucket_id "test_b"
  test_popuplate_bucket_helper $token $bucket_id "test_v"
  test_popuplate_bucket_helper $token $bucket_id "test_v/more_tests"
  test_popuplate_bucket_helper $token $bucket_id "develop"

  local src_path="test_file.txt"

  if [ -f $src_path ]; then
    rm $src_path
  fi

  echo "${token}" >> $src_path

  local directory="feature/transactions_service_refactor_suites/0.0/Android"
  local url_suffix="${bucket_id}/root/${directory}"
  ubicloud_upload_file $token $src_path $url_suffix
}

function test_popuplate_bucket_helper
{
  local token=$1
  local bucket_id=$2
  local directory=$3
  local src_path="test_file.txt"
  local url_suffix="${bucket_id}/root/${directory}"

  if [ -f $src_path ]; then
    rm $src_path
  fi

  echo "${token}" >> $src_path
  ubicloud_upload_file $token $src_path "${url_suffix}/1.0/Android"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.0/iOS"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.1/Android"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.1/iOS"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.2/Android"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.2/iOS"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.3/Android"
  ubicloud_upload_file $token $src_path "${url_suffix}/1.3/iOS"
}

#######################################################################
# Main 
#######################################################################

g_operation=""
g_verbose=false
g_exit_upon_error=false
g_platform=""
g_bucket_id=""
g_relative_path=""
g_min_version=""

# Process commandline arguments
while getopts 'b:ehm:o:p:q:r:u:v' opt; do
    case "$opt" in
      b)
        g_bucket_id="$OPTARG"
        ;;

      e)
        g_exit_upon_error=true
        ;;

      m)
        g_min_version="$OPTARG"
        ;;

      o)
        g_operation="$OPTARG"
        ;;

      p)
        g_password="$OPTARG"
        ;;

      q)
        g_platform="$OPTARG"
        ;;

      r)
        g_relative_path="$OPTARG"
        ;;

      u)
        g_username="$OPTARG"
        ;;

      v)
        g_verbose=true
        ;;

      ?|h)
        echo "Usage: $(basename $0) [-e] [-v] [-u username] [-p password] [-o operation] [-b bucketId] [-r relativePath] [-q platform] [-m minimumVersion]"
        exit 1
        ;;
    esac
done
shift "$(($OPTIND -1))"

if [ "$g_verbose" = true ]; then
 set -x
fi

print_header "Ubisoft Cloud arguments"
echo "g_username: ${g_username}"
echo "g_password: ${g_password}"
echo "g_operation: ${g_operation}"
echo "g_bucket_id: ${g_bucket_id}"
echo "g_exit_upon_error: ${g_exit_upon_error}"
echo "g_verbose: ${g_verbose}"
echo "g_relative_path: ${g_relative_path}"
echo "g_min_version: ${g_min_version}"

print_header "Ubisoft Cloud processing ${g_operation} ..."

ubicloud_retrieve_token $g_username $g_password

if [[ "$g_token" == "" ]]; then
  ubicloud_add_error "Token couldn't be retrieved. Make sure that username and password are correct."
else
  print_upload=false
  print_delete=false

  if [ $g_operation = "upload_from_settings" ]; then
    print_upload=true
    ubicloud_upload_platform_from_settings $g_token $g_platform
  elif [ $g_operation = "delete" ]; then
    print_delete=true
    ubicloud_delete_directory $g_token $g_bucket_id "${g_relative_path}" "On demand"
  elif [ $g_operation = "delete_versions" ]; then
    print_delete=true
    ubicloud_delete_versions $g_token $g_bucket_id $g_min_version
  elif [ $g_operation = "delete_stale_branches" ]; then  
    print_delete=true
    ubicloud_delete_stale_branches $g_token $g_bucket_id
  elif [ $g_operation = "clean_up" ]; then  
    print_delete=true
    ubicloud_clean_up $g_token $g_bucket_id $g_min_version
  elif [ $g_operation = "test_populate_bucket" ]; then
    print_upload=true
    test_populate_bucket $g_token $g_bucket_id
  else
    echo "${operation} operation not supported"
  fi
fi
#echo "Token: $g_token"

ubicloud_print_result "$print_upload" "$print_delete"

if [[ "$g_ubicloud_errors" != "" && "$g_exit_upon_error" = true ]]; then
  exit 1
fi
