#!/usr/bin/env bash
set -eu

evidence=${1:?evidence directory is required}
root=/work/erofs-freebsd-pre
dut=$root/repo-pre-15
base=/work/build/vm-freebsd-build.qcow2.bp
askpass=/work/build/.repo22-ssh-askpass
module=${B21_MODULE:-$root/planning/pre15/evidence/20260816T-B21-abi15-build/B21-erofs-zstdio0.ko}
source_tree=${B21_FREEBSD_SRC:-/work/dev-freebsd-releng}
owned_pid=
port=
loaded=0
mounted=0
md_units=

test ! -e "$evidence"
mkdir -p "$evidence"
: >"$evidence/cleanup.log"

ssh_args=(env DISPLAY=:0 SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force ssh
	-n
    -o BatchMode=no -o PubkeyAuthentication=no
    -o PreferredAuthentications=keyboard-interactive,password
    -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
    -o LogLevel=ERROR -o ConnectTimeout=5)
scp_args=(env DISPLAY=:0 SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force scp
    -O -q -o BatchMode=no -o PubkeyAuthentication=no
    -o PreferredAuthentications=keyboard-interactive,password
    -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
    -o LogLevel=ERROR -o ConnectTimeout=5)

guest()
{
	timeout -k 5 60 "${ssh_args[@]}" -p "$port" root@127.0.0.1 "$@"
}

capture()
{
	local name=$1
	shift
	set +e
	guest "$@" >"$evidence/$name.stdout" 2>"$evidence/$name.stderr"
	local rc=$?
	set -e
	printf '%s\n' "$rc" >"$evidence/$name.rc"
}

scp_to()
{
	local source_path=$1
	local destination_path=$2
	timeout -k 5 60 "${scp_args[@]}" -P "$port" "$source_path" \
		"root@127.0.0.1:$destination_path"
}

cleanup()
{
	set +e
	local cleanup_rc=0
	if test "$mounted" = 1; then guest umount /mnt/pre15-b21 >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; fi
	for md in $md_units; do guest mdconfig -d -u "$md" >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; done
	if test "$loaded" = 1; then guest kldunload /root/B21-erofs-zstdio0.ko >>"$evidence/cleanup.log" 2>&1 || cleanup_rc=1; fi
	if test -n "$owned_pid" && kill -0 "$owned_pid" 2>/dev/null; then
		test "$owned_pid" != 26318 || exit 99
		kill "$owned_pid" 2>/dev/null
		wait "$owned_pid" 2>/dev/null
	fi
	test ! -e "$evidence/owned-overlay.qcow2" || unlink "$evidence/owned-overlay.qcow2"
	python3 - "$port" <<'PY'
import socket
import sys
if not sys.argv[1]:
    raise SystemExit(0)
with socket.socket() as sock:
    sock.settimeout(0.2)
    raise SystemExit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) else 1)
PY
	test $? = 0 || cleanup_rc=1
	stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \
		>"$evidence/base-metadata-after.txt"
	cmp -s "$evidence/base-metadata-before.txt" "$evidence/base-metadata-after.txt" || cleanup_rc=1
	if test "$cleanup_rc" = 0 && test ! -e "$evidence/owned-overlay.qcow2"; then
		printf '%s\n' PASS >"$evidence/cleanup.status"
	else
		printf '%s\n' FAIL >"$evidence/cleanup.status"
	fi
}
trap cleanup EXIT HUP INT TERM

test -x "$askpass"
test -f "$module"
test -f "$base"
stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \
	>"$evidence/base-metadata-before.txt"
git -C "$dut" rev-parse HEAD >"$evidence/dut-head.txt"
{
	git -C "$source_tree" log -1 --format='%H %cs %s'
	sed -n '76,77p' "$source_tree/sys/sys/param.h"
	sed -n '53,54p' "$source_tree/sys/conf/newvers.sh"
} >"$evidence/source-identity.txt"
printf '%s\n' 'SSH options precede root@127.0.0.1 and the remote command' \
	>"$evidence/transport-contract.txt"

python3 -B "$dut/tests/pre15/fixtures/B21-super-generate.py" \
	--spec "$dut/tests/pre15/fixtures/B21-super-spec.json" \
	--output "$evidence/first" --work "$evidence/first-work" \
	>"$evidence/generate-first.stdout" 2>"$evidence/generate-first.stderr"
python3 -B "$dut/tests/pre15/fixtures/B21-super-generate.py" \
	--spec "$dut/tests/pre15/fixtures/B21-super-spec.json" \
	--output "$evidence/second" --work "$evidence/second-work" \
	>"$evidence/generate-second.stdout" 2>"$evidence/generate-second.stderr"
cmp "$evidence/first/fixture-index.json" "$evidence/second/fixture-index.json"
python3 -B "$dut/tests/pre15/fixtures/B21-super-oracle.py" \
	--fixtures "$evidence/first" --report "$evidence/oracle.json" \
	>"$evidence/oracle.stdout" 2>"$evidence/oracle.stderr"

python3 - "$evidence/first/fixture-index.json" "$evidence/oracle.json" \
	"$evidence/matrix.tsv" <<'PY'
import json
import sys
from pathlib import Path

index = json.loads(Path(sys.argv[1]).read_text())
oracle = json.loads(Path(sys.argv[2]).read_text())
assert index["case_count"] == 13 and oracle["passed_count"] == 13
rows = ["id\texpected_errno\treject"]
for fixture, result in zip(index["cases"], oracle["results"], strict=True):
    rows.append(
        f"{fixture['id']}\t{result['actual_errno']}\t"
        f"{fixture['expected_reject']}"
    )
Path(sys.argv[3]).write_text("\n".join(rows) + "\n")
PY
tar -C "$evidence/first" -czf "$evidence/fixtures.tar.gz" .

qemu-img create -f qcow2 -F qcow2 -b "$base" \
	"$evidence/owned-overlay.qcow2" >"$evidence/qemu-img.stdout" \
	2>"$evidence/qemu-img.stderr"
port=$(python3 - <<'PY'
import socket
with socket.socket() as sock:
    sock.bind(("127.0.0.1", 0))
    port = sock.getsockname()[1]
    if port == 9222:
        raise SystemExit("protected port selected")
    print(port)
PY
)
printf '%s\n' "$port" >"$evidence/owned-port.txt"
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 -m 6144 -smp 4 \
	-drive "file=$evidence/owned-overlay.qcow2,if=virtio,format=qcow2" \
	-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$port-:22" \
	-device virtio-net-pci,netdev=net0 -display none \
	-serial "file:$evidence/serial.log" -monitor none \
	>"$evidence/qemu.stdout" 2>"$evidence/qemu.stderr" &
owned_pid=$!
printf '%s\n' "$owned_pid" >"$evidence/owned-pid.txt"
test "$owned_pid" != 26318

ready=0
for attempt in $(seq 1 180); do
	if ! kill -0 "$owned_pid" 2>/dev/null; then exit 21; fi
	set +e
	guest true >"$evidence/readiness.stdout" 2>"$evidence/readiness.stderr"
	readiness_rc=$?
	set -e
	printf '%s\n' "$readiness_rc" >"$evidence/readiness.rc"
	if test "$readiness_rc" = 0; then ready=1; break; fi
	sleep 1
done
printf 'attempts=%s ready=%s\n' "$attempt" "$ready" >"$evidence/readiness.txt"
test "$ready" = 1

capture guest-uname uname -a
capture guest-freebsd-version freebsd-version -kru
capture guest-osreldate sysctl -n kern.osreldate
capture guest-kldstat-before kldstat
capture guest-kldstat-module-before kldstat -q -m erofs
capture guest-dmesg-before dmesg
test "$(cat "$evidence/guest-kldstat-module-before.rc")" = 1

scp_to "$module" /root/B21-erofs-zstdio0.ko \
	>"$evidence/upload-module.stdout" 2>"$evidence/upload-module.stderr"
scp_to "$dut/tests/pre15/fixtures/B21-qemu-probe.c" /root/B21-qemu-probe.c \
	>"$evidence/upload-probe.stdout" 2>"$evidence/upload-probe.stderr"
scp_to "$evidence/fixtures.tar.gz" /root/B21-fixtures.tar.gz \
	>"$evidence/upload-fixtures.stdout" 2>"$evidence/upload-fixtures.stderr"
capture guest-module-file file /root/B21-erofs-zstdio0.ko
capture guest-module-sha256 sha256 -q /root/B21-erofs-zstdio0.ko
capture guest-prepare sh -c \
	'rm -rf /root/B21-fixtures /mnt/pre15-b21; mkdir /root/B21-fixtures /mnt/pre15-b21; tar -xzf /root/B21-fixtures.tar.gz -C /root/B21-fixtures; cc -O2 -Wall -Wextra -Werror -std=c17 -o /root/B21-qemu-probe /root/B21-qemu-probe.c'
test "$(cat "$evidence/guest-prepare.rc")" = 0

capture B21-kldload kldload /root/B21-erofs-zstdio0.ko
test "$(cat "$evidence/B21-kldload.rc")" = 0
loaded=1
capture guest-kldstat-after-load kldstat
capture guest-kldstat-module-after-load kldstat -q -m erofs
capture guest-dmesg-after-load dmesg
test "$(cat "$evidence/guest-kldstat-module-after-load.rc")" = 0

printf 'id\texpected_errno\tactual_errno\treject\n' \
	>"$evidence/qemu-matrix.tsv"
while IFS="$(printf '\t')" read -r id expected_errno reject; do
	test "$id" != id || continue
	set +e
	md=$(guest mdconfig -a -t vnode -f "/root/B21-fixtures/$id.erofs" \
		2>"$evidence/$id-md.stderr")
	md_rc=$?
	set -e
	printf '%s\n' "$md_rc" >"$evidence/$id-md.rc"
	test "$md_rc" = 0
	md_units="$md_units $md"
	set +e
	probe_result=$(guest /root/B21-qemu-probe "/dev/$md" /mnt/pre15-b21 \
		2>"$evidence/$id-probe.stderr")
	probe_rc=$?
	set -e
	printf '%s\n' "$probe_rc" >"$evidence/$id-probe.rc"
	printf '%s\n' "$probe_result" >"$evidence/$id-probe.stdout"
	actual_errno=$(printf '%s\n' "$probe_result" | \
		sed -n 's/.*errno=\([0-9][0-9]*\).*/\1/p')
	test -n "$actual_errno"
	printf '%s\t%s\t%s\t%s\n' "$id" "$expected_errno" "$actual_errno" \
		"$reject" >>"$evidence/qemu-matrix.tsv"
	test "$actual_errno" = "$expected_errno"
	if test "$actual_errno" = 0; then
		mounted=1
		guest find /mnt/pre15-b21 -mindepth 1 -maxdepth 1 -print \
			>"$evidence/$id-readdir.txt"
		guest umount /mnt/pre15-b21
		mounted=0
	fi
	guest mdconfig -d -u "$md"
	md_units=$(printf '%s\n' "$md_units" | sed "s/ $md//")
done <"$evidence/matrix.tsv"

capture guest-dmesg-after dmesg
capture guest-kldstat-after kldstat
capture guest-kldstat-module-after kldstat -q -m erofs
diff -u "$evidence/guest-dmesg-before.stdout" \
	"$evidence/guest-dmesg-after.stdout" >"$evidence/dmesg.diff" || true
if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free' \
	"$evidence/dmesg.diff"; then
	exit 10
fi
guest kldunload /root/B21-erofs-zstdio0.ko >"$evidence/guest-kldunload.stdout" \
	2>"$evidence/guest-kldunload.stderr"
loaded=0
capture guest-kldstat-module-clean kldstat -q -m erofs
capture guest-mount-clean mount -p
capture guest-md-clean mdconfig -l
test "$(cat "$evidence/guest-kldstat-module-clean.rc")" = 1
test ! -s "$evidence/guest-md-clean.stdout"
test -z "$(awk '$3 == "erofs" { print }' "$evidence/guest-mount-clean.stdout")"
printf '%s\n' reached >"$evidence/target.marker"
printf '%s\n' 'B21_PASS TC172-super-trust 13/13' >"$evidence/RESULT.txt"
