#!/usr/bin/env bash
set -euo pipefail

release_base_url=${ANIM_RELEASE_BASE_URL:-https://anim.bhat.ca/releases/latest}
release_base_url=${release_base_url%/}
install_dir=${ANIM_INSTALL_DIR:-"${HOME:?HOME is not set}/.local/bin"}
config_root=${XDG_CONFIG_HOME:-"${HOME:?HOME is not set}/.config"}
anim_config_dir=$config_root/anim

case "$install_dir" in
    /*) ;;
    *)
        echo "anim: ANIM_INSTALL_DIR must be an absolute path" >&2
        exit 1
        ;;
esac
if [[ $install_dir == *:* || $install_dir == *$'\n'* || $install_dir == *$'\r'* ]]; then
    echo "anim: ANIM_INSTALL_DIR contains a character that PATH cannot represent safely" >&2
    exit 1
fi
case "$config_root" in
    /*) ;;
    *)
        echo "anim: XDG_CONFIG_HOME must be an absolute path" >&2
        exit 1
        ;;
esac

case "$release_base_url" in
    https://*) ;;
    http://*|file://*)
        if [[ ${ANIM_ALLOW_INSECURE:-0} != 1 ]]; then
            echo "anim: refusing a non-HTTPS release URL" >&2
            exit 1
        fi
        ;;
    *)
        echo "anim: release URL must use HTTPS" >&2
        exit 1
        ;;
esac

for command_name in curl tar mktemp mkdir mv chmod awk grep uname sort cp rm; do
    if ! command -v "$command_name" >/dev/null 2>&1; then
        echo "anim: required command is missing: $command_name" >&2
        exit 1
    fi
done

case "$(uname -s)" in
    Linux) platform_os=linux ;;
    Darwin) platform_os=macos ;;
    *)
        echo "anim: setup.sh supports Linux and macOS; use the Windows archive directly" >&2
        exit 1
        ;;
esac

case "$(uname -m)" in
    x86_64|amd64) platform_arch=x86_64 ;;
    arm64|aarch64) platform_arch=aarch64 ;;
    *)
        echo "anim: unsupported CPU architecture: $(uname -m)" >&2
        exit 1
        ;;
esac

platform=$platform_os-$platform_arch
archive_name=anim-$platform.tar.gz
download_url=$release_base_url/$archive_name
checksum_url=$download_url.sha256

validate_profile() {
    local profile_path=$1
    local begin_marker='# >>> anim setup >>>'
    local end_marker='# <<< anim setup <<<'
    local begin_count
    local end_count
    local managed_block
    local expected_block

    if [[ ! -f $profile_path ]]; then
        return
    fi
    begin_count=$(grep -Fxc "$begin_marker" "$profile_path" || true)
    end_count=$(grep -Fxc "$end_marker" "$profile_path" || true)
    if [[ $begin_count -eq 0 && $end_count -eq 0 ]]; then
        return
    fi
    if [[ $begin_count -ne 1 || $end_count -ne 1 ]]; then
        echo "anim: refusing to modify invalid setup markers in $profile_path" >&2
        exit 1
    fi

    managed_block=$(awk -v begin="$begin_marker" -v end="$end_marker" '
        $0 == begin { inside = 1 }
        inside { print }
        $0 == end { inside = 0 }
    ' "$profile_path")
    expected_block=$(printf '%s\n' \
        "$begin_marker" \
        '[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/anim/env" ] && . "${XDG_CONFIG_HOME:-$HOME/.config}/anim/env"' \
        "$end_marker")
    if [[ $managed_block != "$expected_block" ]]; then
        echo "anim: refusing to modify a customized setup block in $profile_path" >&2
        exit 1
    fi
}

validate_profile "$HOME/.profile"
case "${SHELL:-}" in
    */bash)
        validate_profile "$HOME/.bashrc"
        if [[ -e $HOME/.bash_profile ]]; then
            validate_profile "$HOME/.bash_profile"
        fi
        ;;
    */zsh)
        validate_profile "$HOME/.zshrc"
        validate_profile "$HOME/.zprofile"
        ;;
esac

download() {
    curl \
        --proto '=https' \
        --tlsv1.2 \
        --fail \
        --silent \
        --show-error \
        --location \
        --retry 3 \
        --connect-timeout 15 \
        --max-time 300 \
        "$@"
}

if [[ $release_base_url == http://* || $release_base_url == file://* ]]; then
    download() {
        curl \
            --fail \
            --silent \
            --show-error \
            --location \
            --retry 3 \
            --connect-timeout 15 \
            --max-time 300 \
            "$@"
    }
fi

version=$(download "$release_base_url/version")
if [[ ! $version =~ ^0\.1\.[0-9]+\+g[0-9a-f]{12}$ ]]; then
    echo "anim: server returned an invalid release version" >&2
    exit 1
fi

temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/anim-setup.XXXXXXXX")
install_temporary=
config_temporary=
cleanup() {
    rm -rf "$temporary_dir"
    if [[ -n $install_temporary ]]; then
        rm -f "$install_temporary"
    fi
    if [[ -n $config_temporary ]]; then
        rm -f "$config_temporary"
    fi
}
trap cleanup EXIT HUP INT TERM

archive_path=$temporary_dir/$archive_name
download --output "$archive_path" "$download_url"
expected_checksum=$(download "$checksum_url")
if [[ ! $expected_checksum =~ ^[0-9a-f]{64}$ ]]; then
    echo "anim: server returned an invalid SHA-256 checksum" >&2
    exit 1
fi

if command -v sha256sum >/dev/null 2>&1; then
    actual_checksum=$(sha256sum "$archive_path" | awk '{ print $1 }')
elif command -v shasum >/dev/null 2>&1; then
    actual_checksum=$(shasum -a 256 "$archive_path" | awk '{ print $1 }')
elif command -v openssl >/dev/null 2>&1; then
    actual_checksum=$(openssl dgst -sha256 "$archive_path" | awk '{ print $NF }')
else
    echo "anim: sha256sum, shasum, or openssl is required" >&2
    exit 1
fi

if [[ $actual_checksum != "$expected_checksum" ]]; then
    echo "anim: checksum verification failed; the existing installation is unchanged" >&2
    exit 1
fi

expected_members=$(printf '%s\n' \
    "anim-$platform/" \
    "anim-$platform/anim" \
    "anim-$platform/LICENSE.txt" | sort)
actual_members=$(tar -tzf "$archive_path" | sort)
if [[ $actual_members != "$expected_members" ]]; then
    echo "anim: release archive has unexpected contents" >&2
    exit 1
fi

tar -xzf "$archive_path" -C "$temporary_dir"
downloaded_binary=$temporary_dir/anim-$platform/anim
chmod 0755 "$downloaded_binary"
if [[ $("$downloaded_binary" --version) != "anim $version" ]]; then
    echo "anim: downloaded binary does not report the expected version" >&2
    exit 1
fi

mkdir -p "$install_dir"
install_temporary=$(mktemp "$install_dir/.anim.install.XXXXXXXX")
cp "$downloaded_binary" "$install_temporary"
chmod 0755 "$install_temporary"
mv -f "$install_temporary" "$install_dir/anim"
install_temporary=

mkdir -p "$anim_config_dir"
env_file=$anim_config_dir/env
config_temporary=$(mktemp "$anim_config_dir/.env.XXXXXXXX")
escaped_install_dir=${install_dir//\'/\'\\\'\'}
cat >"$config_temporary" <<EOF
# Managed by https://anim.bhat.ca/setup.sh
anim_bin_dir='$escaped_install_dir'
case ":\${PATH:-}:" in
  *":\${anim_bin_dir}:"*) ;;
  *) PATH="\${anim_bin_dir}\${PATH:+":\$PATH"}"; export PATH ;;
esac
unset anim_bin_dir
EOF
chmod 0600 "$config_temporary"
mv -f "$config_temporary" "$env_file"
config_temporary=

ensure_posix_profile() {
    local profile_path=$1
    local begin_marker='# >>> anim setup >>>'
    local end_marker='# <<< anim setup <<<'

    if [[ -f $profile_path ]] && grep -Fqx "$begin_marker" "$profile_path"; then
        return
    fi

    mkdir -p "$(dirname "$profile_path")"
    {
        if [[ -s $profile_path ]]; then
            printf '\n'
        fi
        printf '%s\n' \
            "$begin_marker" \
            '[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/anim/env" ] && . "${XDG_CONFIG_HOME:-$HOME/.config}/anim/env"' \
            "$end_marker"
    } >>"$profile_path"
}

ensure_posix_profile "$HOME/.profile"
case "${SHELL:-}" in
    */bash)
        ensure_posix_profile "$HOME/.bashrc"
        if [[ -e $HOME/.bash_profile ]]; then
            ensure_posix_profile "$HOME/.bash_profile"
        fi
        ;;
    */zsh)
        ensure_posix_profile "$HOME/.zshrc"
        ensure_posix_profile "$HOME/.zprofile"
        ;;
    */fish)
        fish_config=$config_root/fish/conf.d/anim.fish
        mkdir -p "$(dirname "$fish_config")"
        printf "fish_add_path -g '%s'\n" "$escaped_install_dir" >"$fish_config"
        ;;
esac

PATH=$install_dir${PATH:+:$PATH}
export PATH

printf 'anim %s installed at %s\n' "$version" "$install_dir/anim"
printf 'PATH setup is persistent; restart your shell to use anim everywhere.\n'
