#!/bin/sh
# script/fetch-assets: download the third-party browser assets the web UI
# ships and install them under static/. Minified bundles are not committed
# (REPO_POLICIES.md: no build artifacts in version control), so the build
# fetches them here. Every download is verified against a hardcoded sha256
# before it is installed, and any mismatch aborts. Idempotent: an asset
# already present with its pinned hash is left alone.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"

# The sha256 of each installed asset lives in static/vendor.sha256, in
# sha256sum(1) format, with paths relative to static/. That file is the
# single source of truth: this script verifies against it, and
# static/vendor_test.go asserts the bytes embedded into the binary match
# it, so the hash cannot rot into a value nothing checks.
MANIFEST="static/vendor.sha256"

# Alpine.js 3.14.9, 2026-08-17. Fetched from registry.npmjs.org, the
# publisher of record; the jsDelivr and unpkg copies are mirrors of this
# same tarball. dist/cdn.min.js is the browser build Alpine publishes for
# a <script> tag.
ALPINE_VERSION="3.14.9"
ALPINE_URL="https://registry.npmjs.org/alpinejs/-/alpinejs-${ALPINE_VERSION}.tgz"
# sha256 of alpinejs-3.14.9.tgz
ALPINE_TARBALL_SHA256="97dad7c0c81e659cfc8e7700055da9770f8186187cb9a8a76efb57e00d5ce52a"
ALPINE_MEMBER="package/dist/cdn.min.js"
ALPINE_DEST="js/alpine.min.js"

sha256_of() {
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$1" | cut -d' ' -f1
    else
        shasum -a 256 "$1" | cut -d' ' -f1
    fi
}

# expected_sha256 <path-relative-to-static>
expected_sha256() {
    awk -v want="$1" '$2 == want { print $1; found = 1 }
        END { if (!found) exit 1 }' "$ROOT/$MANIFEST"
}

# verify <file> <expected-sha256> <what>
verify() {
    actual="$(sha256_of "$1")"
    if [ "$actual" != "$2" ]; then
        echo "fetch-assets: sha256 mismatch for $3" >&2
        echo "  expected: $2" >&2
        echo "  actual:   $actual" >&2
        exit 1
    fi
}

# up_to_date <path-relative-to-static> <expected-sha256>
up_to_date() {
    [ -f "$ROOT/static/$1" ] || return 1
    [ "$(sha256_of "$ROOT/static/$1")" = "$2" ]
}

fetch_alpine() {
    want="$(expected_sha256 "$ALPINE_DEST")"

    if up_to_date "$ALPINE_DEST" "$want"; then
        echo "fetch-assets: static/$ALPINE_DEST already at $want"
        return 0
    fi

    echo "fetch-assets: fetching Alpine.js $ALPINE_VERSION from $ALPINE_URL"
    tmp="$(mktemp -d)"
    trap 'rm -rf "$tmp"' EXIT INT TERM
    curl -fsSL -o "$tmp/alpine.tgz" "$ALPINE_URL"
    verify "$tmp/alpine.tgz" "$ALPINE_TARBALL_SHA256" "alpinejs-${ALPINE_VERSION}.tgz"
    tar -xzOf "$tmp/alpine.tgz" "$ALPINE_MEMBER" >"$tmp/alpine.min.js"
    verify "$tmp/alpine.min.js" "$want" "$ALPINE_MEMBER from alpinejs-${ALPINE_VERSION}.tgz"

    mkdir -p "$(dirname "$ROOT/static/$ALPINE_DEST")"
    cp "$tmp/alpine.min.js" "$ROOT/static/$ALPINE_DEST"
    rm -rf "$tmp"
    trap - EXIT INT TERM
    echo "fetch-assets: installed static/$ALPINE_DEST ($want)"
}

# Re-check every manifest entry against what is now on disk, so an entry
# no script installs fails loudly instead of passing silently.
verify_manifest() {
    while read -r want path; do
        case "$want" in '' | '#'*) continue ;; esac
        if [ ! -f "$ROOT/static/$path" ]; then
            echo "fetch-assets: $MANIFEST lists static/$path, which is missing" >&2
            exit 1
        fi
        verify "$ROOT/static/$path" "$want" "static/$path"
    done <"$ROOT/$MANIFEST"
}

main() {
    cd "$ROOT"
    fetch_alpine
    verify_manifest
    echo "fetch-assets: all assets in $MANIFEST verified"
}

main "$@"
