#!/bin/bash

############################################################
#
# Find the differences of instance A and instance B and produce helper files.
# Note: This script does not use AWS CLI but instead uses results from
#   connect_save on instance A and B.
#

VERSION=1.3.5
SCRIPT_VERSION="$(basename $0) $VERSION"

USAGE=$(cat <<EOD
Usage: $(basename $0) [-?fev] [-l lambda_prefix_a=lambda_prefix_b] [-b lex_bot_prefix_a=lex_bot_prefix_b] instance_alias_a instance_alias_b helper
    Based on connect_save result on Amazon Connect instance A and B,
    find the differences and produce helper files to safely copy components from A to B.

    instance_alias_a    Alias of the Connect instance A
    instance_alias_b    Alias of the Connect instance B
                        (Aliases can be a path to the directory where the instance was saved using connect_save.)
    helper              Name of the helper directory
    -f                  Force removal of existing helper directory
    -e                  Proceed even when the system may not encode Extended ASCII characters properly
    -v                  Show version of this script
    -l lambda_prefix_a=lambda_prefix_b
                        Lambda function name prefixes for instances A and B (if different) to be replaced during copying
    -b lex_bot_prefix_a=lex_bot_prefix_b
                        Lex bot (Classic) name prefixes for instances A and B (if different) to be replaced during copying
    -?                  Help

    Note: This script create files in the helper directory without changing any instance component files.
EOD
)
usage() { echo -e "$USAGE" >&2; exit 2; }
version() { echo -e "$SCRIPT_VERSION"; exit; }
dos2unix() { tr -d '\r'; }

error() { echo -e "Error: $1" >&2; exit 1; }

hex_cmd=
if [ -x "$(command -v xxd)" ]; then
  hex_cmd="xxd -u -p -c1"
elif [ -x "$(command -v hexdump)" ]; then
  hex_cmd="hexdump -v -e '/1 \"%02X\n\"'"
elif [ -x "$(command -v od)" ]; then
  hex_cmd="od -An -vtx1 | tr [:lower:] [:upper:] | for i in \$(cat); do echo \$i; done"
fi
test -z "$hex_cmd" && error "Cannot find any hex conversion commands. Please install one of these: xxd, hexdump, od"
hex_code() { printf '%s' "$1" | eval "$hex_cmd" | while read x; do printf "%%%s" "$x"; done }

path_encode()  {
    old_lc_collate=$LC_COLLATE
    LC_COLLATE=C
    local length="${#1}"
    for (( i = 0; i < length; i++ )); do
        local c="${1:$i:1}"
        case $c in
            [a-zA-Z0-9.~_-]) printf '%s' "$c";;
            *) x=$(hex_code "$c"); echo -n ${x//%0D/};;
        esac
    done
    LC_COLLATE=$old_lc_collate
}

add_file() {
    alias_dir="$1"
    file_suffix="$2"
    helper_file="$3"
    if [ ! -s "$alias_dir/$file_suffix" ]; then
        error "File missing or empty: $alias_dir/$file_suffix"
        exit 1
    else
        echo "$file_suffix" >> $helper_file
    fi
}

forced=
ignore_improper_extended_ascii=
lambda_prefix_a=
lambda_prefix_b=
lex_bot_prefix_a=
lex_bot_prefix_b=
while getopts "?fevl:b:" arg; do
    case "$arg" in
    f)  forced=on;;
    e)  ignore_improper_extended_ascii=on;;
    v)  version;;
    l)  lambda_prefix_a=${OPTARG%%=*}
        lambda_prefix_b=${OPTARG#*=}
        ;;
    b)  lex_bot_prefix_a=${OPTARG%%=*}
        lex_bot_prefix_b=${OPTARG#*=}
        ;;
    *)  usage;;
    esac
done
shift $((OPTIND-1))

if [ "$(hex_code "é")" != "%C3%A9" ]; then
    echo "WARNING: This system may not encode Extended ASCII characters properly." >&2
    if [ -n "$ignore_improper_extended_ascii" ]; then
        echo "Proceed regardless as the -e option is specified." >&2
    else
        cat <<EOD >&2

If your instance component names contain Extended ASCII characters, such as accented letters
like é, this system will encode those names differently from standard encoding.

If you are sure that your component names do not contain Extended ASCII characters,
you may proceed regardless by running the command again with the -e option.
EOD
        exit 1
    fi
fi

instance_alias_dir_a=$1
instance_alias_a=$(basename "$instance_alias_dir_a")
instance_alias_dir_b=$2
instance_alias_b=$(basename "$instance_alias_dir_b")
# helper=${3:-${instance_alias_a}_TO_${instance_alias_b}}
helper=$3

# Backward compatibility - if they are set, ignore the arguments
lambda_prefix_a=${lambda_prefix_a:-$4}
lambda_prefix_b=${lambda_prefix_b:-$5}

if [ -z "$instance_alias_a" -o -z "$instance_alias_b" -o -z "$helper" ]; then
    usage
fi

if [ -d $helper ]; then
    if [ -n "$forced" ]; then
        echo "Existing Helper directory forcefully removed: $helper"
        rm -fr $helper
    else
        error "Helper directory already exists. Remove and try again (or use -f): $helper"
    fi
fi

# routing_profile_to_ignore="Basic Routing Profile"
flow_template=$helper/flow_template.json
module_template=$helper/module_template.json

cat <<EOD
Instance Alias A: $instance_alias_a (in directory "$instance_alias_dir_a")
Instance Alias B: $instance_alias_b (in directory "$instance_alias_dir_b")
Helper: $helper
EOD

errorMsg=
if [ ! -d "$instance_alias_dir_a" ]; then
    errorMsg="$errorMsg \"$instance_alias_dir_a\""
fi
if [ ! -d "$instance_alias_dir_b" ]; then
    errorMsg="$errorMsg \"$instance_alias_dir_b\""
fi
if [ -n "$errorMsg" ]; then
    errorMsg="Cannot find instance directories:$errorMsg"
    error "$errorMsg"
fi

mkdir -p $helper

for ext in var sed new old; do
    eval helper_$ext=$helper/helper.$ext
    eval fn=\$helper_$ext
    > $fn
done


############################################################
#
# Instance
#

echo "instance_alias_a=\"$instance_alias_a\"" | tee -a $helper_var
echo "instance_alias_dir_a=\"$instance_alias_dir_a\"" | tee -a $helper_var
echo "instance_alias_b=\"$instance_alias_b\"" | tee -a $helper_var
echo "instance_alias_dir_b=\"$instance_alias_dir_b\"" | tee -a $helper_var

. "$instance_alias_dir_a/instance.var"

instance_id_a=$instance_Id
instance_arn_a=$instance_Arn
eval $(echo $instance_arn_a | (IFS=: read x x x r a x; echo "region_a=$r; aws_ac_a=$a"))
region_a=$region_a
aws_ac_a=$aws_ac_a
echo "instance_id_a=\"$instance_id_a\"" | tee -a $helper_var
echo "instance_arn_a=\"$instance_arn_a\"" | tee -a $helper_var
echo "region_a=\"$region_a\"" | tee -a $helper_var
echo "aws_ac_a=\"$aws_ac_a\"" | tee -a $helper_var
echo "aws_profile_a=\"$aws_Profile\"" | tee -a $helper_var
echo "lambda_prefix_a=\"$lambda_prefix_a\"" | tee -a $helper_var
echo "lex_bot_prefix_a=\"$lex_bot_prefix_a\"" | tee -a $helper_var

. "$instance_alias_dir_b/instance.var"
instance_id_b=$instance_Id
instance_arn_b=$instance_Arn
eval $(echo $instance_arn_b | (IFS=: read x x x r a x; echo "region_b=$r; aws_ac_b=$a"))
region_b=$region_b
aws_ac_b=$aws_ac_b
echo "instance_id_b=\"$instance_id_b\"" | tee -a $helper_var
echo "instance_arn_b=\"$instance_arn_b\"" | tee -a $helper_var
echo "region_b=\"$region_b\"" | tee -a $helper_var
echo "aws_ac_b=\"$aws_ac_b\"" | tee -a $helper_var
echo "aws_profile_b=\"$aws_Profile\"" | tee -a $helper_var
echo "lambda_prefix_b=\"$lambda_prefix_b\"" | tee -a $helper_var
echo "lex_bot_prefix_b=\"$lex_bot_prefix_b\"" | tee -a $helper_var

# Use Instance B contact flow prefix
echo "contact_flow_prefix=\"$instance_contact_flow_prefix\"" | tee -a $helper_var


############################################################
#
# General SED commands
#

connect_arn_prefix_a="arn:aws:connect:$region_a:$aws_ac_a"
connect_arn_prefix_b="arn:aws:connect:$region_b:$aws_ac_b"
cat <<EOD >> $helper_sed
# General SED commands
s%$instance_id_a%$instance_id_b%g
s%$connect_arn_prefix_a%$connect_arn_prefix_b%g
EOD

lambda_arn_prefix_a="arn:aws:lambda:$region_a:$aws_ac_a:function:$lambda_prefix_a"
lambda_arn_prefix_b="arn:aws:lambda:$region_b:$aws_ac_b:function:$lambda_prefix_b"
if [ "$lambda_arn_prefix_a" != "$lambda_arn_prefix_b" ]; then
    echo "s%$lambda_arn_prefix_a%$lambda_arn_prefix_b%g" >> $helper_sed
fi

echo

############################################################
#
# Prompts
#

echo Checking Prompts ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/prompts.json" |
dos2unix |
while read prompt_id_a prompt_name; do
    prompt_name_encoded=$(path_encode "$prompt_name")
    # prompt_id_b=$(jq -r "select(.Name == \"${prompt_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/prompts.json")
    prompt_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/prompts.json" | jq -r "select(.Name == \"${prompt_name//\"/%22}\") | .Id" | dos2unix)
    if [ -z "$prompt_id_b" ]; then
        echo "prompt_$prompt_name" >> $helper_new
    else
        echo "prompt_$prompt_name" >> $helper_old
        cat <<EOD >> $helper_sed
# Prompt: $prompt_name
s%$prompt_id_a%$prompt_id_b%g
EOD
    fi
done
test $? -eq 0 || error


############################################################
#
# Hours of operations
#

echo Checking Hours of operations ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/hours.json" |
dos2unix |
while read hour_id_a hour_name; do
    hour_name_encoded=$(path_encode "$hour_name")
    # hour_id_b=$(jq -r "select(.Name == \"${hour_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/hours.json")
    hour_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/hours.json" | jq -r "select(.Name == \"${hour_name//\"/%22}\") | .Id" | dos2unix)
    if [ -z "$hour_id_b" ]; then
        add_file "$instance_alias_dir_a" "hour_$hour_name_encoded.json" $helper_new
    else
        add_file "$instance_alias_dir_b" "hour_$hour_name_encoded.json" $helper_old
        cat <<EOD >> $helper_sed
# Hour of operation: $hour_name
s%$hour_id_a%$hour_id_b%g
EOD
    fi
done
test $? -eq 0 || error


############################################################
#
# Queues
#

echo Checking Queues ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/queues.json" |
dos2unix |
while read queue_id_a queue_name; do
    queue_name_encoded=$(path_encode "$queue_name")
    # queue_id_b=$(jq -r "select(.Name == \"${queue_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/queues.json")
    queue_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/queues.json" | jq -r "select(.Name == \"${queue_name//\"/%22}\") | .Id" | dos2unix)
    if [ -z "$queue_id_b" ]; then
        add_file "$instance_alias_dir_a" "queue_$queue_name_encoded.json" $helper_new
    else
        add_file "$instance_alias_dir_b" "queue_$queue_name_encoded.json" $helper_old
        cat <<EOD >> $helper_sed
# Queue: $queue_name
s%$queue_id_a%$queue_id_b%g
EOD
    fi
done
test $? -eq 0 || error


############################################################
#
# Routing Profiles
#

echo Checking Routing Profiles ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/routings.json" |
dos2unix |
while read routing_id_a routing_name; do
    # if [ "$routing_name" == "$routing_profile_to_ignore" ]; then
    #     continue
    # fi
    routing_name_encoded=$(path_encode "$routing_name")
    # routing_id_b=$(jq -r "select(.Name == \"${routing_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/routings.json")
    routing_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/routings.json" | jq -r "select(.Name == \"${routing_name//\"/%22}\") | .Id" | dos2unix)
    if [ -z "$routing_id_b" ]; then
        add_file "$instance_alias_dir_a" "routing_$routing_name_encoded.json" $helper_new
        add_file "$instance_alias_dir_a" "routingQs_$routing_name_encoded.json" $helper_new
    else
        add_file "$instance_alias_dir_b" "routing_$routing_name_encoded.json" $helper_old
        add_file "$instance_alias_dir_b" "routingQs_$routing_name_encoded.json" $helper_old
        cat <<EOD >> $helper_sed
# Routing Profile: $routing_name
s%$routing_id_a%$routing_id_b%g
EOD
    fi
done
test $? -eq 0 || error


############################################################
#
# Contact Flow Modules
#

echo Checking Contact Flow Modules ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/modules.json" |
dos2unix |
while read module_id_a module_name; do
    module_name_encoded=$(path_encode "$module_name")
    # module_id_b=$(jq -r "select(.Name == \"${module_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/modules.json")
    module_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/modules.json" | jq -r "select(.Name == \"${module_name//\"/%22}\") | .Id" | dos2unix)
    if [ -z "$module_id_b" ]; then
        add_file "$instance_alias_dir_a" "module_$module_name_encoded.json" $helper_new
    else
        add_file "$instance_alias_dir_b" "module_$module_name_encoded.json" $helper_old
        cat <<EOD >> $helper_sed
# Contact Flow Module: $module_name
s%$module_id_a%$module_id_b%g
EOD
    fi
done
test $? -eq 0 || error


############################################################
#
# Contact Flows
#

echo Checking Contact Flows ...
jq -r ".Id + \" \" + .Name" "$instance_alias_dir_a/flows.json" |
dos2unix |
while read flow_id_a flow_name; do
    # flow_id_b=$(jq -r "select(.Name == \"${flow_name//\"/\\\"}\") | .Id" "$instance_alias_dir_b/flows.json")
    flow_id_b=$(sed -e's/\\"/%22/g' "$instance_alias_dir_b/flows.json" | jq -r "select(.Name == \"${flow_name//\"/%22}\") | .Id" | dos2unix)
    flow_name_encoded=$(path_encode "$flow_name")
    if [ -z "$flow_id_b" ]; then
        add_file "$instance_alias_dir_a" "flow_$flow_name_encoded.json" $helper_new
    else
        add_file "$instance_alias_dir_b" "flow_$flow_name_encoded.json" $helper_old
        cat <<EOD >> $helper_sed
# Contact Flow: $flow_name
s%$flow_id_a%$flow_id_b%g
EOD
    fi
done
test $? -eq 0 || error

cat > $flow_template <<EOD
{
    "Version": "2019-10-30",
    "StartAction": "e093fb75-2263-4594-875e-2d8e9974595f",
    "Metadata": {
        "entryPointPosition": {
            "x": 20,
            "y": 20
        },
        "snapToGrid": false,
        "ActionMetadata": {
            "e093fb75-2263-4594-875e-2d8e9974595f": {
                "position": {
                    "x": 120,
                    "y": 20
                }
            }
        }
    },
    "Actions": [
        {
            "Identifier": "e093fb75-2263-4594-875e-2d8e9974595f",
            "Type": "DisconnectParticipant",
            "Parameters": {},
            "Transitions": {}
        }
    ]
}
EOD

cat > $module_template <<EOD
{
  "Version": "2019-10-30",
  "StartAction": "13f850a8-4882-4b78-ac83-508273b6a3d6",
  "Metadata": {
    "entryPointPosition": {
      "x": 20,
      "y": 20
    },
    "ActionMetadata": {
      "13f850a8-4882-4b78-ac83-508273b6a3d6": {
        "position": {
          "x": 120,
          "y": 20
        }
      }
    }
  },
  "Actions": [
    {
      "Parameters": {},
      "Identifier": "13f850a8-4882-4b78-ac83-508273b6a3d6",
      "Type": "EndFlowModuleExecution",
      "Transitions": {}
    }
  ],
  "Settings": {
    "InputParameters": [],
    "OutputParameters": [],
    "Transitions": []
  }
}
EOD

cat <<EOD

These helper files are created:
$helper_var: About the source and target Amazon Connect instances
$helper_new: Components to add to "$instance_alias_dir_b"
$helper_old: Components to update for "$instance_alias_dir_b" based on "$instance_alias_dir_a"
$helper_sed: SED script used to update components
$flow_template: Contact Flow template json

All done
EOD