#!/bin/sh
set -eu

umask 077

require_variable() {
    variable_name="$1"
    eval "variable_value=\${$variable_name:-}"

    if [ -z "$variable_value" ]; then
        echo "$variable_name is required." >&2
        exit 1
    fi
}

aws_cli() {
    endpoint="${BACKUP_S3_ENDPOINT:-${AWS_ENDPOINT:-}}"

    if [ -n "$endpoint" ]; then
        aws --endpoint-url "$endpoint" "$@"
    else
        aws "$@"
    fi
}

for variable in \
    AWS_ACCESS_KEY_ID \
    AWS_DEFAULT_REGION \
    AWS_SECRET_ACCESS_KEY \
    BACKUP_S3_BUCKET \
    DB_DATABASE \
    DB_HOST \
    DB_PASSWORD \
    DB_USERNAME
do
    require_variable "$variable"
done

backup_directory="${BACKUP_DIRECTORY:-/backups}"
backup_prefix="${BACKUP_S3_PREFIX:-database-backups/postgres}"
backup_prefix="${backup_prefix#/}"
backup_prefix="${backup_prefix%/}"
backup_label="${1:-manual}"
backup_label="$(printf "%s" "$backup_label" | tr -cd "A-Za-z0-9_-")"
retention_days="${BACKUP_LOCAL_RETENTION_DAYS:-7}"
timestamp="$(date -u +"%Y%m%dT%H%M%SZ")"
database_name="$(printf "%s" "$DB_DATABASE" | tr -cd "A-Za-z0-9_-")"
backup_name="${timestamp}-${database_name}-${backup_label:-manual}.dump"
backup_path="$backup_directory/$backup_name"
temporary_path="$backup_path.partial"
checksum_path="$backup_path.sha256"
lock_directory="/tmp/bowli-database-backup.lock"

case "$retention_days" in
    ""|*[!0-9]*)
        echo "BACKUP_LOCAL_RETENTION_DAYS must be a positive integer." >&2
        exit 1
        ;;
esac

if ! mkdir "$lock_directory" 2>/dev/null; then
    echo "Another database backup is already running." >&2
    exit 1
fi

cleanup() {
    rm -f "$temporary_path"
    rmdir "$lock_directory" 2>/dev/null || true
}

trap cleanup EXIT INT TERM

mkdir -p "$backup_directory"

export PGPASSWORD="$DB_PASSWORD"
export PGCONNECT_TIMEOUT="${PGCONNECT_TIMEOUT:-10}"

echo "Creating PostgreSQL backup: $backup_name"
pg_dump \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --dbname="$DB_DATABASE" \
    --format=custom \
    --compress=9 \
    --no-owner \
    --no-acl \
    --file="$temporary_path"

pg_restore --list "$temporary_path" >/dev/null
mv "$temporary_path" "$backup_path"

(
    cd "$backup_directory"
    sha256sum "$backup_name" > "$backup_name.sha256"
)

s3_directory="s3://$BACKUP_S3_BUCKET"
if [ -n "$backup_prefix" ]; then
    s3_directory="$s3_directory/$backup_prefix"
fi

echo "Uploading backup to $s3_directory/"
aws_cli s3 cp \
    "$backup_path" \
    "$s3_directory/$backup_name" \
    --sse AES256 \
    --only-show-errors
aws_cli s3 cp \
    "$checksum_path" \
    "$s3_directory/$backup_name.sha256" \
    --sse AES256 \
    --only-show-errors

touch "$backup_directory/.last-success"

find "$backup_directory" -type f \
    \( -name "*.dump" -o -name "*.dump.sha256" \) \
    -mtime "+$retention_days" \
    -delete

echo "Database backup completed: $backup_name"
