#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2026 Bryan Joshua Pedini

# fake zypper (unit tests). FAKE_ZYPPER_LU=fixture|real|none picks the list-updates XML,
# FAKE_ZYPPER_DUP=fixture|empty|removes the `dist-upgrade --dry-run` plan (data/zypper/dup-*.xml).
# addlock writes real zypp lock entries into the locksfile.path of $ZYPP_CONF (default
# /etc/zypp/locks is never touched: the fake refuses); update/dist-upgrade log that file.
# info replays data/zypper/info-NAME.txt (exit 104 without one); FAKE_ZYPPER_INFO_RC=7 makes it fail
# like a zypp lock held by another process.
# shellcheck source=../fakelib.bash
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
fake_log "${ZYPP_CONF:+ZYPP_CONF=$ZYPP_CONF }$*"
D=$FAKE_DATA/zypper

locksfile() {
	local f=${ZYPP_CONF:-} line l=
	[[ -n $f && -r $f ]] || return 1
	while IFS= read -r line; do
		[[ $line =~ ^[[:space:]]*locksfile\.path[[:space:]]*=[[:space:]]*(.*)$ ]] && l=${BASH_REMATCH[1]}
	done <"$f"
	[[ -n $l ]] || return 1
	printf '%s' "$l"
}

pos=()
for a in "$@"; do case $a in -*) ;; *) pos+=("$a") ;; esac; done
verb=${pos[0]:-}
args=("${pos[@]:1}")
xml_remove() { # the install-summary of `remove --dry-run` for the given names
	local n
	printf "<?xml version='1.0'?>\n<stream>\n<install-summary download-size=\"0\" space-usage-diff=\"-30\" packages-to-change=\"%s\">\n<to-remove>\n" "$#"
	for n in "$@"; do printf '<solvable type="package" name="%s" edition="1.0-1" arch="noarch" repository="@System" summary="dummy %s"><description>d</description></solvable>\n' "$n" "$n"; done
	printf '</to-remove>\n</install-summary>\n</stream>\n'
}
dry=0
case " $* " in *' --dry-run '*) dry=1 ;; esac

case $verb in
refresh)
	rc=$(fake_rc FAKE_ZYPPER_REFRESH_RC 0)
	((rc == 0)) || echo "Could not refresh the repositories because of errors." >&2
	exit "$rc"
	;;
list-updates) cat "$D/lu-${FAKE_ZYPPER_LU:-fixture}.xml" ;;
info)
	p=${args[-1]}
	if [[ -n ${FAKE_ZYPPER_INFO_RC:-} ]]; then
		echo "System management is locked by the application with pid 4242 (zypper)."
		echo "Close this application before trying again."
		exit "$FAKE_ZYPPER_INFO_RC"
	fi
	if [[ -f $D/info-$p.txt ]]; then cat "$D/info-$p.txt"; else
		echo "package '$p' not found."
		exit 104
	fi
	;;
addlock)
	l=$(locksfile) || {
		echo "fake zypper: addlock without a private ZYPP_CONF/locksfile.path" >&2
		exit 98
	}
	for p in "${args[@]}"; do printf '\ntype: package\nmatch_type: glob\ncase_sensitive: on\nsolvable_name: %s\n\n' "$p" >>"$l"; done
	;;
dist-upgrade | dup | update)
	if ((dry)); then
		cat "$D/dup-${FAKE_ZYPPER_DUP:-fixture}.xml"
		exit 0
	fi
	if l=$(locksfile); then { echo "--- locks seen by $verb:"; cat "$l"; echo "--- end"; } >>"$FAKE_LOG"; fi
	if [[ -n ${ZYPP_CONF:-} && -n ${SKEPTICAL_UPDATER_STATE_DIR:-} && -e $SKEPTICAL_UPDATER_STATE_DIR/zypper.lock ]]; then
		if flock -n "$SKEPTICAL_UPDATER_STATE_DIR/zypper.lock" true; then l=free; else l=held; fi
		echo "zypper.lock during $verb: $l" >>"$FAKE_LOG"
	fi
	if [[ -n ${FAKE_ZYPPER_UPGRADE_KILL:-} ]]; then
		kill -s "$FAKE_ZYPPER_UPGRADE_KILL" "$PPID"
		sleep 0.2
	fi
	exit "$(fake_rc FAKE_ZYPPER_UPGRADE_RC 0)"
	;;
remove)
	if ((dry)); then
		set -- "${args[@]}"
		# FAKE_ZYPPER_CLEAN="pkg:dep" (unneeded dependency) / FAKE_ZYPPER_EXTRA="pkg:other" join the plan
		more=()
		for r in ${FAKE_ZYPPER_CLEAN:-} ${FAKE_ZYPPER_EXTRA:-}; do [[ ${r%%:*} == "${1:-}" ]] && more+=("${r#*:}"); done
		case ${1:-} in
		su-dummy-b) xml_remove su-dummy-a su-dummy-b "${more[@]}" ;;
		su-dummy-*) xml_remove "$1" "${more[@]}" ;;
		*)
			echo "'$1' not found in package names. Trying capabilities."
			echo "No provider of '$1' found."
			exit 104
			;;
		esac
		exit 0
	fi
	for p in "${args[@]}"; do
		case $p in su-dummy-*) ;; *)
			echo "'$p' not found in package names. Trying capabilities."
			echo "No provider of '$p' found."
			exit 104
			;;
		esac
	done
	fake_mark_purged "${args[@]}"
	;;
*)
	echo "fake zypper: unhandled: $*" >&2
	exit 99
	;;
esac
