#!/bin/sh
# Product-owned consumer bootstrap. Generated by `sc-lint init --just`.
set -eu

usage() {
    printf '%s\n' 'usage: .sc-lint/bootstrap <setup|lint|test|upgrade> --config sc-lint.toml [--check] [--dry-run]' >&2
    exit 2
}

operation="${1:-}"
[ -n "$operation" ] || usage
shift
config=""
selector=""
flags=""

while [ "$#" -gt 0 ]; do
    case "$1" in
        --config)
            [ "$#" -ge 2 ] || usage
            config="$2"
            shift 2
            ;;
        --check|--dry-run) flags="$flags $1"; shift ;;
        *) [ -z "$selector" ] || usage; selector="$1"; shift ;;
    esac
done
# Re-expose the mode flags as "$@" for the setup/upgrade paths below.
# shellcheck disable=SC2086
set -- $flags

[ -n "$config" ] || usage
case "$operation" in setup|lint|test|upgrade) ;; *) usage ;; esac
case "$operation" in lint|test) ;; *) [ -z "$selector" ] || usage ;; esac

minimum_version() {
    sed -n 's/^[[:space:]]*minimum_version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$config" | head -n 1
}

install_dir="${SC_LINT_INSTALL_DIR:-${XDG_DATA_HOME:-${HOME:-}/.local/share}/sc-lint/bin}"
managed_binary="$install_dir/sc-lint"
product_binary=""

# Python helper package (sc_lint wheel) provisioned next to the config file.
venv_dir="$(cd "$(dirname "$config")" && pwd)/.sc-lint/venv"
venv_python="$venv_dir/bin/python3"

host_python() {
    command -v python3 2>/dev/null || command -v python 2>/dev/null
}

venv_ready() {
    [ -x "$venv_python" ] && "$venv_python" -c 'import sys, sc_lint
parse = lambda value: tuple(int(part) for part in value.split("+")[0].split(".")[:3])
sys.exit(0 if parse(sc_lint.__version__) >= parse(sys.argv[1]) else 1)' "$1" >/dev/null 2>&1
}

venv_mode() {
    for argument in "$@"; do
        case "$argument" in
            --check) echo check; return ;;
            --dry-run) echo dry-run; return ;;
        esac
    done
    echo install
}

# provision_venv <install|check|dry-run>: ensure .sc-lint/venv holds sc-lint>=minimum_version.
# SC_LINT_WHEEL_DIR selects an offline wheel directory instead of PyPI.
provision_venv() {
    version="$(minimum_version)"
    if venv_ready "$version"; then return 0; fi
    case "$1" in
        check)
            printf '%s\n' "setup: $venv_dir does not provide sc-lint>=$version (CLI.SC_LINT_PYTHON_UNAVAILABLE)" >&2
            return 4
            ;;
        dry-run)
            printf '%s\n' "setup: would install sc-lint==$version into $venv_dir"
            return 0
            ;;
    esac
    if [ ! -x "$venv_python" ]; then
        python="$(host_python)"
        if [ -z "$python" ] || ! "$python" -m venv "$venv_dir"; then
            printf '%s\n' "setup: could not create $venv_dir (CLI.SC_LINT_PYTHON_UNAVAILABLE)" >&2
            return 4
        fi
    fi
    if [ -n "${SC_LINT_WHEEL_DIR:-}" ]; then
        set -- --no-index --find-links "$SC_LINT_WHEEL_DIR"
    else
        set --
    fi
    if ! "$venv_python" -m pip install --quiet --disable-pip-version-check "$@" "sc-lint==$version"; then
        printf '%s\n' "setup: could not install sc-lint==$version into $venv_dir (CLI.SC_LINT_PYTHON_UNAVAILABLE)" >&2
        return 4
    fi
}

resolve_product_binary() {
    if [ -n "${SC_LINT_BIN:-}" ] && command -v "$SC_LINT_BIN" >/dev/null 2>&1; then
        product_binary="$(command -v "$SC_LINT_BIN")"
    elif [ -x "$managed_binary" ]; then
        product_binary="$managed_binary"
    elif command -v sc-lint >/dev/null 2>&1; then
        product_binary="$(command -v sc-lint)"
    else
        product_binary=""
    fi
}

bootstrap_install() {
    command="$1"
    shift
    version="$(minimum_version)"
    [ -n "$version" ] || {
        printf '%s\n' "setup: could not read minimum_version from $config (CLI.SC_LINT_CONFIG_MALFORMED)" >&2
        exit 3
    }
    case "$(uname -s)/$(uname -m)" in
        Linux/x86_64) target="x86_64-unknown-linux-gnu" ;;
        Darwin/x86_64) target="x86_64-apple-darwin" ;;
        Darwin/arm64) target="aarch64-apple-darwin" ;;
        *)
            printf '%s\n' "setup: no verified sc-lint release for $(uname -s)/$(uname -m) (CLI.SC_LINT_INSTALL_UNSUPPORTED_PLATFORM)" >&2
            exit 4
            ;;
    esac
    archive="sc-lint_${version}_${target}.tar.gz"
    base="${SC_LINT_RELEASE_BASE_URL:-https://github.com/randlee/sc-lint/releases/download}"
    release_url="${base%/}/v${version}"
    staging="$(mktemp -d "${TMPDIR:-/tmp}/sc-lint-bootstrap.XXXXXX")"
    cleanup() { rm -rf "$staging"; }
    trap cleanup EXIT HUP INT TERM
    if ! curl --fail --location --retry 3 --output "$staging/checksums.txt" "$release_url/checksums.txt"; then
        printf '%s\n' "setup: could not download checksum manifest (CLI.SC_LINT_RELEASE_UNAVAILABLE)" >&2
        exit 4
    fi
    if ! curl --fail --location --retry 3 --output "$staging/$archive" "$release_url/$archive"; then
        printf '%s\n' "setup: could not download $archive (CLI.SC_LINT_RELEASE_UNAVAILABLE)" >&2
        exit 4
    fi
    expected="$(awk -v name="$archive" '$2 == name || $2 == "*" name { print $1; exit }' "$staging/checksums.txt")"
    [ -n "$expected" ] || {
        printf '%s\n' "setup: checksum manifest did not contain $archive (CLI.SC_LINT_RELEASE_UNAVAILABLE)" >&2
        exit 4
    }
    if command -v sha256sum >/dev/null 2>&1; then actual="$(sha256sum "$staging/$archive" | awk '{print $1}')"; else actual="$(shasum -a 256 "$staging/$archive" | awk '{print $1}')"; fi
    [ "$actual" = "$expected" ] || {
        printf '%s\n' "setup: SHA-256 verification failed for $archive (CLI.SC_LINT_RELEASE_CHECKSUM_MISMATCH)" >&2
        exit 4
    }
    if ! mkdir -p "$staging/extract"; then
        printf '%s\n' "setup: could not create release launcher directory (CLI.SC_LINT_INSTALL_PERMISSION_DENIED)" >&2
        exit 4
    fi
    if ! tar -xzf "$staging/$archive" -C "$staging/extract"; then
        printf '%s\n' "setup: could not extract $archive (CLI.SC_LINT_RELEASE_UNAVAILABLE)" >&2
        exit 4
    fi
    launcher="$staging/extract/sc-lint"
    if [ ! -f "$launcher" ]; then
        launcher="$staging/extract/sc-lint_${version}_${target}/bin/sc-lint"
    fi
    [ -f "$launcher" ] || {
        printf '%s\n' "setup: release archive did not contain sc-lint (CLI.SC_LINT_RELEASE_UNAVAILABLE)" >&2
        exit 4
    }
    # The extracted release is only a launcher. Its Rust installer owns the
    # managed-target backup, activation, rollback, and version verification.
    "$launcher" --config "$config" "$command" "$@"
}

ensure_product() {
    resolve_product_binary
    if [ -z "$product_binary" ]; then
        bootstrap_install setup "$@"
        product_binary="$managed_binary"
    fi
    if "$product_binary" --config "$config" compatibility check --binary "$product_binary"; then return; fi
    if [ "$product_binary" = "$managed_binary" ]; then
        for argument in "$@"; do
            if [ "$argument" = "--check" ] || [ "$argument" = "--dry-run" ]; then
                "$product_binary" --config "$config" setup "$@"
                return
            fi
        done
        bootstrap_install setup "$@"
    else
        "$product_binary" --config "$config" setup
    fi
    [ -x "$managed_binary" ] && product_binary="$managed_binary"
}

activate_product_env() {
    export SC_LINT_BIN="$product_binary"
    PATH="$(dirname "$product_binary"):$PATH"; export PATH
}

case "$operation" in
    setup)
        ensure_product "$@"
        "$product_binary" --config "$config" setup "$@" || exit $?
        provision_venv "$(venv_mode "$@")"
        exit $?
        ;;
    lint)
        ensure_product "$@"
        activate_product_env
        provision_venv install || exit $?
        if [ -n "$selector" ]; then exec "$product_binary" lint --consumer --config "$config" ci --profile "$selector"; fi
        exec "$product_binary" lint --consumer --config "$config" ci
        ;;
    test)
        ensure_product "$@"
        activate_product_env
        provision_venv install || exit $?
        if [ -n "$selector" ]; then exec "$product_binary" test --config "$config" "$selector"; fi
        exec "$product_binary" test --config "$config"
        ;;
    upgrade)
        resolve_product_binary
        if [ -z "$product_binary" ]; then
            bootstrap_install upgrade "$@"
            exit $?
        fi
        if ! "$product_binary" --config "$config" compatibility check --binary "$product_binary" && [ "$product_binary" = "$managed_binary" ]; then
            for argument in "$@"; do
                if [ "$argument" = "--check" ] || [ "$argument" = "--dry-run" ]; then
                    exec "$product_binary" --config "$config" upgrade "$@"
                fi
            done
            bootstrap_install upgrade "$@"
            exit $?
        fi
        "$product_binary" --config "$config" upgrade "$@" || exit $?
        provision_venv "$(venv_mode "$@")"
        exit $?
        ;;
esac
