#!/usr/bin/env bash
#
# update.sh - point the PKGBUILD at the current yandex-browser stable release.
#
# The Yandex APT pool only ever keeps the newest .deb, so a pinned pkgver goes
# stale (404) as soon as upstream publishes a build.  This script asks the pool
# for the current file name, rewrites pkgver / _debrel / the .deb checksum in
# the PKGBUILD and drops the archive into makepkg's source cache, so the build
# that follows reuses it instead of fetching ~190 MiB a second time.
#
#   ./update.sh            update the PKGBUILD if a newer release exists
#   ./update.sh --check    only report current vs. available version
#   ./update.sh --force    rewrite even when the version did not change
#   ./update.sh --crx      also drop cached .crx files so extensions refresh
#
# 250409 sfs

set -Eeuo pipefail

readonly POOL_URL='https://repo.yandex.ru/yandex-browser/deb/pool/main/y/yandex-browser-stable'
readonly DEB_SUM_MARKER='# deb: managed by ./update.sh'

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly script_dir
readonly pkgbuild="${script_dir}/PKGBUILD"

log() { printf '==> %s\n' "$*"; }
warn() { printf '==> WARNING: %s\n' "$*" >&2; }
die() {
	printf '==> ERROR: %s\n' "$*" >&2
	exit 1
}

usage() {
	# the comment block right below the shebang
	sed -n '2,/^[^#]/{/^#/{s/^# \?//;p}}' "${BASH_SOURCE[0]}"
}

# Directory makepkg downloads sources into - honour SRCDEST from makepkg.conf,
# otherwise sources live next to the PKGBUILD.
srcdest() {
	local conf value
	for conf in /etc/makepkg.conf "${XDG_CONFIG_HOME:-${HOME}/.config}/pacman/makepkg.conf" "${HOME}/.makepkg.conf"; do
		[[ -r ${conf} ]] || continue
		value="$(
			set +u
			# shellcheck disable=SC1090 # runtime path, resolved above
			. "${conf}" >/dev/null 2>&1 || true
			printf '%s' "${SRCDEST:-}"
		)"
		[[ -n ${value} ]] && [[ -d ${value} ]] && {
			printf '%s' "${value}"
			return
		}
	done
	printf '%s' "${script_dir}"
}

# Newest yandex-browser-stable_<pkgver>-<debrel>_amd64.deb offered by the pool.
remote_deb() {
	local listing name
	listing="$(wget -qO- "${POOL_URL}/")" || die "cannot read ${POOL_URL}/"
	name="$(
		printf '%s\n' "${listing}" |
			grep -oE 'yandex-browser-stable_[0-9][0-9.]*-[0-9]+_amd64\.deb' |
			sort -V | tail -n1
	)"
	[[ -n ${name} ]] || die "no .deb found in ${POOL_URL}/ - did the layout change?"
	printf '%s' "${name}"
}

# Value of a plain `name=value` assignment in the PKGBUILD.
pkgbuild_value() {
	local value
	value="$(sed -n "s/^$1=[\"']\?\([^\"'#[:space:]]*\).*/\1/p" "${pkgbuild}" | head -n1)"
	[[ -n ${value} ]] || die "cannot read $1 from ${pkgbuild}"
	printf '%s' "${value}"
}

download_deb() {
	local name="$1" dir="$2" dest="${2}/${1}"

	if [[ -s ${dest} ]]; then
		log "reusing cached ${name}"
		return
	fi

	log "downloading ${name} -> ${dir}"
	mkdir -p "${dir}"
	if ! wget -q --show-progress -O "${dest}.part" "${POOL_URL}/${name}"; then
		rm -f "${dest}.part"
		die "download of ${name} failed"
	fi
	mv -f "${dest}.part" "${dest}"
}

# Rewrite the three release-dependent fields in place.
rewrite_pkgbuild() {
	local pkgver="$1" debrel="$2" sum="$3" tmp
	tmp="$(mktemp "${pkgbuild}.XXXXXX")"

	sed -e "s/^pkgver=.*/pkgver=${pkgver}/" \
		-e "s/^pkgrel=.*/pkgrel=1/" \
		-e "s/^_debrel=.*/_debrel=${debrel}/" \
		-e "s|^\([[:space:]]*\)'[0-9a-f]\{64\}'\([[:space:]]*\)${DEB_SUM_MARKER}|\1'${sum}'\2${DEB_SUM_MARKER}|" \
		"${pkgbuild}" >"${tmp}"

	local written
	written="$(sed -n "s|^[[:space:]]*'\([0-9a-f]\{64\}\)'[[:space:]]*${DEB_SUM_MARKER}.*|\1|p" "${tmp}" | head -n1)"
	[[ ${written} == "${sum}" ]] || {
		rm -f "${tmp}"
		die "checksum line carrying '${DEB_SUM_MARKER}' not found in ${pkgbuild}"
	}

	chmod --reference="${pkgbuild}" "${tmp}"
	mv -f "${tmp}" "${pkgbuild}"
}

# Extension .crx URLs always serve the newest build, so their checksums are
# SKIP and makepkg keeps reusing whatever it cached first.  Remove the cached
# copies to pull current releases on the next build.
refresh_crx() {
	local dir="$1" id removed=0 ids=()
	mapfile -t ids < <(sed -n "s/^[[:space:]]*'\([a-p]\{32\}\)'.*/\1/p" "${pkgbuild}")
	for id in "${ids[@]}"; do
		[[ -e "${dir}/${id}.crx" ]] || continue
		rm -f "${dir}/${id}.crx"
		removed=$((removed + 1))
	done
	log "dropped ${removed} cached .crx file(s) from ${dir}"
}

main() {
	local check=false force=false crx=false

	while (($#)); do
		case "$1" in
		-c | --check) check=true ;;
		-f | --force) force=true ;;
		-e | --crx | --extensions) crx=true ;;
		-h | --help)
			usage
			return 0
			;;
		*) die "unknown option: $1 (try --help)" ;;
		esac
		shift
	done

	[[ -r ${pkgbuild} ]] || die "no PKGBUILD next to ${BASH_SOURCE[0]}"

	local deb version pkgver debrel cur_pkgver cur_debrel
	deb="$(remote_deb)"
	version="${deb#yandex-browser-stable_}"
	version="${version%_amd64.deb}"
	pkgver="${version%-*}"
	debrel="${version##*-}"

	cur_pkgver="$(pkgbuild_value pkgver)"
	cur_debrel="$(pkgbuild_value _debrel)"

	log "installed in PKGBUILD: ${cur_pkgver}-${cur_debrel}"
	log "available upstream   : ${pkgver}-${debrel}"

	if [[ ${pkgver} == "${cur_pkgver}" && ${debrel} == "${cur_debrel}" ]]; then
		${crx} && refresh_crx "$(srcdest)"
		if ${force}; then
			log "unchanged, but --force given"
		else
			log "PKGBUILD is up to date"
			return 0
		fi
	elif ${check}; then
		${crx} && refresh_crx "$(srcdest)"
		log "update available: ${cur_pkgver}-${cur_debrel} -> ${pkgver}-${debrel}"
		return 1
	fi

	if ${check}; then
		return 0
	fi

	local dir sum
	dir="$(srcdest)"
	download_deb "${deb}" "${dir}"
	sum="$(sha256sum "${dir}/${deb}" | cut -d' ' -f1)"

	rewrite_pkgbuild "${pkgver}" "${debrel}" "${sum}"
	${crx} && refresh_crx "${dir}"

	log "PKGBUILD updated to ${pkgver}-${debrel} (sha256 ${sum:0:16}...)"
	log "run 'makepkg -si' to build"
}

main "$@"
