#!/usr/bin/env bash

# Upload Addressables bundles to DigitalOcean Spaces
# Usage: ./upload_addressables.sh <build_target>
# Example: ./upload_addressables.sh StandaloneOSX
#
# Required environment variables:
#   ACCESS_KEY_ID - DigitalOcean Spaces access key (same as other deploys)
#   SECRET_KEY    - DigitalOcean Spaces secret key (same as other deploys)

set -euxo pipefail

BUILD_TARGET=$1
WORKSPACE=$(pwd)
UNITY_PROJECT="$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
UNITY_VERSION=$(grep "m_EditorVersion:" "$UNITY_PROJECT/ProjectSettings/ProjectVersion.txt" | head -1 | sed 's/m_EditorVersion: //')
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
ADDRESSABLES_PREFIX="addressables/$BUILD_TARGET/$UNITY_MAJOR_MINOR"
SERVER_DATA="$UNITY_PROJECT/ServerData/$BUILD_TARGET/$UNITY_MAJOR_MINOR"

# DigitalOcean Spaces configuration (same region as other eagle0 buckets)
DO_ENDPOINT="https://sfo3.digitaloceanspaces.com"
DO_BUCKET="eagle0-assets"
DEFAULT_MAX_UPLOAD_BYTES=$((1024 * 1024 * 1024))
DEFAULT_MAX_SINGLE_FILE_BYTES=$((512 * 1024 * 1024))
MAX_UPLOAD_BYTES=${ADDRESSABLES_MAX_UPLOAD_BYTES:-$DEFAULT_MAX_UPLOAD_BYTES}
MAX_SINGLE_FILE_BYTES=${ADDRESSABLES_MAX_SINGLE_FILE_BYTES:-$DEFAULT_MAX_SINGLE_FILE_BYTES}
TOP_UPLOAD_COUNT=${ADDRESSABLES_TOP_UPLOAD_COUNT:-20}

COMMON_PATHS="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export PATH="${COMMON_PATHS}:${PATH}"

format_bytes() {
    local bytes=$1
    local unit="B"
    local value=$bytes

    if [ "$bytes" -ge $((1024 * 1024 * 1024)) ]; then
        unit="GiB"
        value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 / 1024 / 1024 }")
    elif [ "$bytes" -ge $((1024 * 1024)) ]; then
        unit="MiB"
        value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 / 1024 }")
    elif [ "$bytes" -ge 1024 ]; then
        unit="KiB"
        value=$(awk "BEGIN { printf \"%.2f\", $bytes / 1024 }")
    fi

    echo "$value $unit"
}

file_size() {
    local path=$1
    if stat -f%z "$path" >/dev/null 2>&1; then
        stat -f%z "$path"
    else
        stat -c%s "$path"
    fi
}

append_summary() {
    if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
        echo "$1" >> "$GITHUB_STEP_SUMMARY"
    fi
}

budget_enabled() {
    local value=$1
    [ "$value" -gt 0 ]
}

summarize_pending_uploads() {
    local sync_args_file
    sync_args_file=$(mktemp)
    local uploads_file
    uploads_file=$(mktemp)
    local sorted_uploads_file
    sorted_uploads_file=$(mktemp)
    trap 'rm -f "$sync_args_file" "$uploads_file" "$sorted_uploads_file"' RETURN

    aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
        --endpoint-url "$DO_ENDPOINT" \
        --acl public-read \
        --size-only \
        --delete \
        --dryrun > "$sync_args_file"

    while IFS= read -r line; do
        case "$line" in
            *upload:*)
                local source_path=${line#*upload: }
                source_path=${source_path% to s3://*}
                if [ -f "$source_path" ]; then
                    local size
                    size=$(file_size "$source_path")
                    printf "%s\t%s\n" "$size" "$source_path" >> "$uploads_file"
                fi
                ;;
        esac
    done < "$sync_args_file"

    local upload_count=0
    local upload_bytes=0
    if [ -s "$uploads_file" ]; then
        while IFS=$'\t' read -r size _path; do
            upload_count=$((upload_count + 1))
            upload_bytes=$((upload_bytes + size))
        done < "$uploads_file"
    fi

    echo "Addressables upload preflight: $upload_count changed files, $(format_bytes "$upload_bytes") to upload."
    append_summary "## Addressables upload preflight"
    append_summary ""
    append_summary "- Target: \`$BUILD_TARGET\`"
    append_summary "- Changed files: \`$upload_count\`"
    append_summary "- Changed upload bytes: \`$(format_bytes "$upload_bytes")\`"
    append_summary "- Upload byte budget: \`$(format_bytes "$MAX_UPLOAD_BYTES")\`"
    append_summary "- Single-file budget: \`$(format_bytes "$MAX_SINGLE_FILE_BYTES")\`"

    local guardrail_failed=0

    if [ -s "$uploads_file" ]; then
        echo "Largest pending Addressables uploads:"
        append_summary ""
        append_summary "### Largest pending uploads"
        append_summary ""
        append_summary "| Size | Path |"
        append_summary "| ---: | --- |"
        sort -nr "$uploads_file" > "$sorted_uploads_file"
        local listed_uploads=0
        while IFS=$'\t' read -r size path; do
            if [ "$listed_uploads" -ge "$TOP_UPLOAD_COUNT" ]; then
                break
            fi
            local relative_path=${path#"$SERVER_DATA"/}
            echo "  $(format_bytes "$size") $relative_path"
            append_summary "| $(format_bytes "$size") | \`$relative_path\` |"
            listed_uploads=$((listed_uploads + 1))
        done < "$sorted_uploads_file"

        if budget_enabled "$MAX_SINGLE_FILE_BYTES"; then
            while IFS=$'\t' read -r size path; do
                if [ "$size" -gt "$MAX_SINGLE_FILE_BYTES" ]; then
                    local relative_path=${path#"$SERVER_DATA"/}
                    echo "ERROR: Pending Addressables upload '$relative_path' is $(format_bytes "$size"), exceeding single-file budget $(format_bytes "$MAX_SINGLE_FILE_BYTES")."
                    append_summary ""
                    append_summary ":x: \`$relative_path\` exceeds the single-file budget at \`$(format_bytes "$size")\`."
                    guardrail_failed=1
                fi
            done < "$uploads_file"
        fi
    fi

    if budget_enabled "$MAX_UPLOAD_BYTES" && [ "$upload_bytes" -gt "$MAX_UPLOAD_BYTES" ]; then
        echo "ERROR: Addressables upload is expected to send $(format_bytes "$upload_bytes"), exceeding budget $(format_bytes "$MAX_UPLOAD_BYTES")."
        echo "A small asset change may have invalidated an oversized bundle; inspect the largest pending uploads above."
        append_summary ""
        append_summary ":x: Changed upload bytes exceed budget: \`$(format_bytes "$upload_bytes")\` > \`$(format_bytes "$MAX_UPLOAD_BYTES")\`."
        guardrail_failed=1
    fi

    if [ "$guardrail_failed" -ne 0 ]; then
        echo "Refusing to upload Addressables. Raise ADDRESSABLES_MAX_UPLOAD_BYTES or ADDRESSABLES_MAX_SINGLE_FILE_BYTES only after confirming the upload is intentional."
        exit 1
    fi
}

if ! command -v aws >/dev/null 2>&1; then
    if command -v brew >/dev/null 2>&1; then
        brew install awscli
    else
        echo "ERROR: aws CLI is unavailable, and Homebrew is not on PATH"
        exit 1
    fi
fi

if [ ! -d "$SERVER_DATA" ]; then
    echo "No Addressables bundles found at $SERVER_DATA"
    echo "Skipping upload (this is expected if Addressables are bundled locally)"
    exit 0
fi

echo "Uploading Addressables bundles from $SERVER_DATA"
echo "Target: s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/"

# Configure AWS CLI for DigitalOcean Spaces
export AWS_ACCESS_KEY_ID="$ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$SECRET_KEY"

summarize_pending_uploads

# Sync bundles to Spaces. Addressable bundle filenames include content hashes, so a
# same-sized existing bundle is already the same content. Use --size-only to avoid
# re-uploading every rebuilt bundle just because Unity gave it a fresh local mtime.
# Metadata files keep stable names, so sync them again without --size-only below.
#
# --delete removes files in destination that don't exist in source.
# --acl public-read makes files publicly accessible.
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
    --endpoint-url "$DO_ENDPOINT" \
    --acl public-read \
    --size-only \
    --delete

aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
    --endpoint-url "$DO_ENDPOINT" \
    --acl public-read \
    --exclude "*" \
    --include "*.bin" \
    --include "*.hash" \
    --include "*.json"

echo "Addressables upload complete"
echo "Files available at: https://assets.eagle0.net/$ADDRESSABLES_PREFIX/"
