#!/usr/bin/env bash
#-*- mode:sh; -*-

########################################################################
usage() {
########################################################################
    cat <<EOF
Usage: $0 options command args

Options
-------
  -h, --help             Display this help message
  -P, --prefix           Path prefix in the S3 bucket
  -b, --bucket           S3 Bucket Name
  -p, --profile          AWS Profile
  -N, --profile-name     orepan2-add config profile name
  -x, --no-invalidate    Do not invalidate cache
  -c, --config           Config file name (default: .orepan2-s3.json)
  -r, --region           AWS region (default: us-east-1)
  -d, --distribution-id  CloudFront distribution id
  -t, --tag              Name tag for CloudFront distribution
  -T, --template         Template for index
  -v, --verbose          Verbose output

Commands
--------
add {file}               Add a tarball to the repository
invalidate               Invalidate CloudFront cache
help                     Display this help message

Notes
-----
1. use -x to when adding a new module to repository to prevent invalidating cache.
   (invalidations are not immediate)

2. default configuration file is .orepan2-s3.json and should be in your home directory

3. you must provide either -t TAG-VALUE or -d DISTRIBUTIO-id

EOF

    exit 1
}

########################################################################
find_config_name() {
########################################################################
    local config_file="$1"
    local config_profile="${2:-default}"

    local path_prefix=".$config_profile.index"

    local index=$(cat $config_file | jq -r "$path_prefix?//\"\"")
    
    if test -n "$index"; then
        echo $config_profile
    fi
}

########################################################################
find_config() {
########################################################################
    local config_file="$1"
    local config_profile="${2:-default}"

    config_profile=$(find_config_name "$config_file" "$config_profile");
    test -n "$config_profile" && config_profile=".$config_profile"

    local path="$config_profile.index? // \"\""

    cat $config_file | jq -r  "$path"
}

########################################################################
upload_artifacts() {
########################################################################

    local bucket="$BUCKET"
    
    local config="$1"

    if [[ -n "$config" && -n $(echo "$config" | jq -r '.files?//""') ]]; then
        local aws_profile="$AWS_PROFILE";

        if test -n "$aws_profile"; then
            aws_profile="--profile $aws_profile"
        fi

        echo "$config" | jq -r '.files| to_entries[] | "\(.key) \(.value)"' |
            while read -r src dest; do
                if test -e "$src"; then
                    echo "uploading $src to => s3://$bucket$dest" >&2
                    aws s3 $aws_profile cp "$src" "s3://$bucket$dest" >&2
                else
                    echo "$src not found...skipping" >&2
                fi
            done
    fi
}

########################################################################
install_config() {
########################################################################
    dist_dir=$(perl -MFile::ShareDir=dist_dir -e 'print dist_dir("OrePAN2-S3");')

    if test -z "$dist_dir"; then
       echo "ERROR: could not find OrePAN2::S3 distribution directory"
       exit 1
    fi
      
    if test -e "$dist_dir/orepan2-s3.json"; then
        echo "installing '.orepan2-s3.json' in $HOME - update this file before proceeding"
        cp "$dist_dir/orepan2-s3.json" "$HOME/.orepan2-s3.json"
    fi

    exit 1
}

########################################################################
find_distribution_id() {
########################################################################
    if test -z "$TAG_VALUE"; then
        echo "$NOT_OK set CloudFront distribution Name tag!"
        usage
    fi

    # Get all CloudFront distribution IDs
    DISTRIBUTION_IDS=$($AWS cloudfront list-distributions --query "DistributionList.Items[*].Id" --profile "$AWS_PROFILE" --output text)


    # Loop through each distribution and check its tags
    for ID in $DISTRIBUTION_IDS; do
        TAG_MATCH=$($AWS cloudfront list-tags-for-resource --profile "$AWS_PROFILE" \
                         --resource "arn:aws:cloudfront::$AWS_ACCOUNT:distribution/$ID" \
                         --query "Tags.Items[?Key=='Name' && Value=='$TAG_VALUE']" --output text)
        
        if [[ -n "$TAG_MATCH" ]]; then
            echo "$OK Found CloudFront Distribution with Name=$TAG_VALUE: $ID"
            DISTRIBUTION_ID="$ID"
            break
        fi
    done

    # Output the result
    if [[ -z "$DISTRIBUTION_ID" ]]; then
        echo "$NOT_OK No CloudFront distribution found with Name=$TAG_VALUE"
        exit 1;
    else
        echo "CloudFront Distribution ID: $DISTRIBUTION_ID"
    fi
}

########################################################################
create_index() {
########################################################################
    index=$(mktemp)
    
    local config_profile=""
    local index_template="$INDEX_TEMPLATE{:-}"

    local config=$(find_config "$CONFIG_FILE" "$PROFILE_NAME")

    if test -n "$PROFILE_NAME"; then
        config_profile="--profile $PROFILE_NAME"
    fi

    local template="${TEMPLATE:-}"

    if [[ -n "$index_template" && -e "$index_template" ]]; then
        template="--template $index_template"
    else
        index_template=$(echo $config | jq -r '.template')
        
        if [[ -n "$index_template" && -e "$index_template" ]]; then
            $template="--template $index_template"
        fi
    fi

    if test -e "$index_template"; then
        test -n "$VERBOSE" && echo "creating index from template $index_template..."
    fi

    orepan2-s3-index $template $config_profile -c $CONFIG_FILE create >$index

    test -n "$VERBOSE" && echo "uploading new index..."
    orepan2-s3-index $config_profile -c $CONFIG_FILE upload $index

    test -n "$VERBOSE" && echo "looking for artifacts to upload..."
    upload_artifacts "$config"

    rm $index
}

########################################################################
invalidate_cache() {
########################################################################
    local output=$($AWS cloudfront create-invalidation \
         --distribution-id "$DISTRIBUTION_ID" \
         --paths "${INVALIDATION_PATHS[@]}" \
         --no-paginate --profile "$AWS_PROFILE")

    test -n "$VERBOSE" && echo "$output" >&2
}

########################################################################
cleanup() {
########################################################################
    test -n "$VERBOSE" && echo "cleaning up temp directory..."
    test -n "$TEMP_DIR" && rm -rf "$TEMP_DIR"
}

########################################################################
add() {
########################################################################
    if test -z "$BUCKET"; then
        echo "$NOT_OK no bucket!";
        exit 1;
    fi

    if test -z "$AWS_PROFILE"; then
        echo "$NOT_OK no profile!";
        exit 1;
    fi

    # Create a temporary directory
    TEMP_DIR=$(mktemp -d)

    # Set an exit trap to always run cleanup
    trap cleanup EXIT

    test -n "$VERBOSE" && echo "syncing s3://$BUCKET/$PATH_PREFIX => $TEMP_DIR/$PATH_PREFIX..."

    # Sync S3 contents to the temporary directory
    $AWS s3 sync "s3://$BUCKET/$PATH_PREFIX" "$TEMP_DIR/$PATH_PREFIX" --profile "$AWS_PROFILE"

    test -n "$VERBOSE" && echo "adding $FILE to OrePAN package index..."

    orepan2-inject "$FILE" "$TEMP_DIR/$PATH_PREFIX"

    # Check if orepan2-inject was successful
    if [[ $? -ne 0 ]]; then
        echo "$NOT_OK orepan2-inject failed." >&2
        exit 1
    fi

    test -n "$VERBOSE" && echo "indexing OrePAN repository..." >&2
    orepan2-indexer "$TEMP_DIR/$PATH_PREFIX"

    test -n "$VERBOSE" && echo "syncing $TEMP_DIR/$PATH_PREFIX => s3://$BUCKET/$PATH_PREFIX..."
    $AWS s3 sync "$TEMP_DIR/$PATH_PREFIX" "s3://$BUCKET/$PATH_PREFIX" --profile "$AWS_PROFILE"
}

########################################################################
set_defaults() {
########################################################################
    local profile_name=$(find_config_name "$CONFIG_FILE" "$PROFILE_NAME")
    local config=$(cat "$CONFIG_FILE" | jq -r ".$profile_name")

    test -z "$AWS_PROFILE" &&  AWS_PROFILE=$(echo "$config" | jq -r '.AWS.profile // ""')
    test -z "$AWS_REGION" && AWS_REGION=$(echo "$config" | jq -r '.AWS.region // "us-east-1"')
    test -z "$DISTRIBUTION_ID" && DISTRIBUTION_ID=$(echo "$config" | jq -r '.CloudFront.DistributionId // ""')
    test -z "$PATH_PREFIX" && PATH_PREFIX=$(echo "$config" | jq -r '.AWS.prefix // ""')
    test -z "$BUCKET" && BUCKET=$(echo "$config" | jq -r '.AWS.bucket // ""')

    AWS_ACCOUNT=$(echo "$CONFIG" | jq -r '.AWS.account // ""')
    test -z "$AWS_ACCOUNT" && AWS_ACCOUNT=$($AWS sts get-caller-identity --query 'Account' --output text --profile "$AWS_PROFILE")

}

########################################################################
# MAIN SCRIPT STARTS HERE
########################################################################
set -oue pipefail

# Initialize variables
CONFIG_FILE=""
FILE=""
TAG_VALUE=""
AWS_PROFILE=""
AWS_REGION=""
DISTRIBUTION_ID=""
PATH_PREFIX=""
BUCKET=""
NO_INVALIDATE=""
NOT_OK="ERROR"
OK=""
INDEX_TEMPLATE=""
VERBOSE=""

# Parse command-line options using getopt
OPTIONS=$(getopt -o hp:t:P:c:d:b:xn:T:v \
                 --long verbose,help,profile:,name:,tag:,template:,prefix:,config:,distribution-id:,bucket:,no-invalidate -- "$@")

if [ $? -ne 0 ]; then
    echo "$NOT_OK Error parsing options." >&2
    exit 1
fi

eval set -- "$OPTIONS"

# Extract options
while true; do
    case "$1" in
        -h|--help)
            usage;
            ;;

        -v|--verbose)
            VERBOSE="1";
            shift 1
            ;;

        -c|--config)
            CONFIG_FILE="$2"
            shift 2
            ;;

        -P|--prefix)
            PATH_PREFIX="$2"
            shift 2
            ;;

        -t|--tag)
            TAG_VALUE="$2"
            shift 2
            ;;

        -T|--template)
            INDEX_TEMPLATE="$2"
            shift 2
            ;;

        -p|--profile)
            AWS_PROFILE="$2"
            shift 2
            ;;

        -d|--distribution-id)
            DISTRIBUTION_ID="$2"
            shift 2
            ;;

        -b|--bucket)
            BUCKET="$2"
            shift 2
            ;;

        -x|--no-invalidate)
            NO_INVALIDATE="$1"
            shift 1
            ;;

        -n|--name)
            PROFILE_NAME="$2"
            shift 2
            ;;

        --)
            shift
            break
            ;;
    esac
done

COMMAND=${1:-}

test -z "$COMMAND" && COMMAND="help"

shift
FILE=${1:-}

AWS=$(command -v aws)

if test -z "$AWS"; then
    echo "$NOT_OK no aws command!"
    exit 1
fi

test -z "$CONFIG_FILE" && CONFIG_FILE="$HOME/.orepan2-s3.json"

if ! test -e "$CONFIG_FILE"; then
    echo "$NOT_OK $CONFIG_FILE not found!"
    install_config
fi

PROFILE_NAME="${PROFILE_NAME:-default}"

CONFIG=$(find_config "$CONFIG_FILE" "$PROFILE_NAME")

set_defaults

INVALIDATION_PATHS=()
INVALIDATION_PATHS+=("/$PATH_PREFIX/modules/02packages.details.txt.gz")
INVALIDATION_PATHS+=("/$PATH_PREFIX/orepan2-cache.json")
INVALIDATION_PATHS+=("/index.html")

for p in $(cat $CONFIG_FILE | jq -r '.default.CloudFront.InvalidationPaths?//[]|.[]|.'); do 
    test -n "$VERBOSE" && echo "adding custom invalidation path: $p" >&2
    INVALIDATION_PATHS+=("$p")
done

if test -n "$FILE"; then
    INVALIDATION_PATHS+=("/$PATH_PREFIX/authors/id/D/DU/DUMMY/$(basename $FILE)")
fi

case "$COMMAND" in 
    help)
        usage
        ;;

    invalidate)
        if test -z "$DISTRIBUTION_ID"; then
            echo "ERROR: No DistributionId in your configuration file."
        else
            test -n "$VERBOSE" && echo "invalidating cache...\n${INVALIDATION_PATHS[@]}"
            invalidate_cache
        fi
        ;;

    add)
        if test -z "$FILE"; then
            echo "$NOT_OK no file specified"
            usage
        fi

        add

        test -n "$VERBOSE" && echo "creating HTML index..." >&2

        create_index

        if test -z "$NO_INVALIDATE"; then
            if test -n "$DISTRIBUTION_ID"; then
                test -n "$VERBOSE" && echo "invalidating cache...\n${INVALIDATION_PATHS[@]}"
                invalidate_cache
            fi
        fi
        ;;
        
    *)
        echo "$NOT_OK no such command $COMMAND"
        exit;
        ;;
esac
