#!/usr/bin/env bash
set -euo pipefail

build_root=$1
source_root=$2

cli_dir="$build_root/src/cli"
for tool in phpefc-php2ast phpefc-phpast2phpir phpefc-phpir2valair phpefc-valair2vala phpefc-valair-optimize-typing phpefc-c-add-source-lines phpefc-generate-library-stub; do
    if [ ! -x "$cli_dir/$tool" ]; then
        echo "missing built PHPEFC tool: $cli_dir/$tool" >&2
        exit 99
    fi
done
for tool in strip readelf; do
    if ! command -v "$tool" >/dev/null 2>&1; then
        echo "missing tool: $tool" >&2
        exit 99
    fi
done
export PATH="$cli_dir:$PATH"

pw_build_meson=${PW_BUILD_MESON:-}
if [ -z "$pw_build_meson" ]; then
    for candidate in \
        "$source_root/../../gentoo-pw-programs/dev-php_pw-build-meson/build/src/pw-build-meson" \
        "$source_root/../gentoo-pw-programs/dev-php_pw-build-meson/build/src/pw-build-meson"; do
        if [ -x "$candidate" ]; then
            pw_build_meson=$candidate
            break
        fi
    done
    if [ -z "$pw_build_meson" ]; then
        pw_build_meson=$(command -v pw-build-meson || true)
    fi
fi
if [ -z "$pw_build_meson" ] || [ ! -x "$pw_build_meson" ]; then
    echo 'missing pw-build-meson; skipping PHPEF library Meson integration smoke' >&2
    exit 77
fi

work=${MESON_TEST_TMPDIR:-}
if [ -z "$work" ]; then
    work=$(mktemp -d)
    trap 'rm -rf "$work"' EXIT
fi
mkdir -p "$work"

prepend_project_header() {
    local project_dir=$1
    python3 - "$project_dir/meson.build" <<'PY'
import sys
from pathlib import Path
p = Path(sys.argv[1])
s = p.read_text()
p.write_text("project('phpef-library-meson-smoke', ['c', 'vala'], version: '0.1')\n" + s)
PY
}

write_common_sources() {
    local root=$1
    local libname=$2
    mkdir -p "$root/lib"
    cat > "$root/lib/main.php" <<PHP
<?php
print("${libname}-top\n");
function ${libname}_answer() {
    print("${libname}-answer\n");
}
PHP
    cat > "$root/main.php" <<PHP
<?php
print("app-main\n");
${libname}_answer();
PHP
}

run_app_and_check() {
    local bin=$1
    local expected=$2
    local out=$3
    "$bin" > "$out"
    if [ "$(cat "$out")" != "$expected" ]; then
        echo 'unexpected app output' >&2
        printf 'expected:\n%s\nactual:\n%s\n' "$expected" "$(cat "$out")" >&2
        exit 1
    fi
}

# Internal one-project smoke: local elib + ebin (library linked via local Meson target/VAPI).
internal="$work/internal"
mkdir -p "$internal"
write_common_sources "$internal" library1
cat > "$internal/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
elib = lib/main.php -> library1
ebin = main.php -> app (library1)
PW
"$pw_build_meson" "$internal" --no-doxygen >/dev/null
prepend_project_header "$internal"
meson setup "$internal/build" "$internal" >/dev/null
CCACHE_DISABLE=1 meson compile -C "$internal/build" >/dev/null
if grep -R -- '--extern-function' "$internal/meson.build" "$internal/build/app.vala" >/dev/null 2>&1; then
    echo 'internal smoke still generated inline --extern-function/extern declaration' >&2
    exit 1
fi
if grep -Fq 'phpef_file_' "$internal/build/library1.vapi"; then
    echo 'internal smoke VAPI leaked phpef_file_* internals' >&2
    cat "$internal/build/library1.vapi" >&2
    exit 1
fi
grep -Fq 'phpef_library_library1_init' "$internal/build/library1.vapi"
grep -Fq 'phpuserfunc_library1_' "$internal/build/library1.vapi"
run_app_and_check "$internal/build/app" $'library1-top\napp-main\nlibrary1-answer' "$internal/app.out"
printf 'ok 1 - one Meson project local PHPEF library dependency builds and runs\n'

# External two-project smoke: library project produces .so/.vapi/stub, app project consumes it via pkg-config + PHPEF_LIBSTUBS_DIR.
external_lib="$work/external-lib"
external_app="$work/external-app"
prefix="$work/prefix"
mkdir -p "$external_lib" "$external_app" "$prefix/lib/pkgconfig" "$prefix/share/vala/vapi" "$prefix/share/phpef/libstubs" "$prefix/include/extlib"
write_common_sources "$external_lib" extlib
cat > "$external_lib/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
elib = lib/main.php -> extlib
PW
"$pw_build_meson" "$external_lib" --no-doxygen >/dev/null
prepend_project_header "$external_lib"
meson setup "$external_lib/build" "$external_lib" >/dev/null
CCACHE_DISABLE=1 meson compile -C "$external_lib/build" >/dev/null
cp "$external_lib/build/libextlib.so" "$prefix/lib/libextlib.so"
cp "$external_lib/build/extlib.vapi" "$prefix/share/vala/vapi/extlib.vapi"
cp "$external_lib/build/extlib.deps" "$prefix/share/vala/vapi/extlib.deps"
cp "$external_lib/build/extlib.php" "$prefix/share/phpef/libstubs/extlib.php"
cp "$external_lib/build/extlib.h" "$prefix/include/extlib/extlib.h"
cat > "$prefix/lib/pkgconfig/extlib.pc" <<PC
prefix=$prefix
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
vapidir=\${prefix}/share/vala/vapi

Name: extlib
Description: PHPEF smoke external library
Version: 0.1
Requires: phpef-runtime-core phpef-runtime-embed
Libs: -L\${libdir} -lextlib
Cflags: -I\${includedir}/extlib
PC
cat > "$external_app/main.php" <<'PHP'
<?php
print("app-main\n");
extlib_answer();
PHP
cat > "$external_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (extlib)
PW
PKG_CONFIG_PATH="$prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$prefix/share/phpef/libstubs" "$pw_build_meson" "$external_app" --no-doxygen >/dev/null
prepend_project_header "$external_app"
PKG_CONFIG_PATH="$prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$external_app/build" "$external_app" >/dev/null
PKG_CONFIG_PATH="$prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$external_app/build" >/dev/null
if grep -R -- '--extern-function' "$external_app/meson.build" "$external_app/build/app.vala" >/dev/null 2>&1; then
    echo 'external smoke still generated inline --extern-function/extern declaration' >&2
    exit 1
fi
grep -Fq "'extlib', '--'" "$external_app/meson.build"
LD_LIBRARY_PATH="$prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" run_app_and_check "$external_app/build/app" $'extlib-top\napp-main\nextlib-answer' "$external_app/app.out"
printf 'ok 2 - two Meson projects external PHPEF .so dependency builds and runs\n'

# External stripped-library stacktrace smoke: the throwing PHP frame lives in a
# linked .so, not in the executable. The library is stripped after its
# .phpef_stacktrace section is embedded; runtime-core must still resolve the
# PHP frame from the loaded library image.
trace_lib="$work/trace-lib"
trace_app="$work/trace-app"
trace_prefix="$work/trace-prefix"
mkdir -p "$trace_lib/lib" "$trace_app" "$trace_prefix/lib/pkgconfig" "$trace_prefix/share/vala/vapi" "$trace_prefix/share/phpef/libstubs" "$trace_prefix/include/tracelib"
cat > "$trace_lib/lib/main.php" <<'PHP'
<?php
function tracelib_boom() {
    throw new \UnexpectedValueException("trace-lib-boom", 1);
}
PHP
cat > "$trace_lib/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
elib = lib/main.php -> tracelib
PW
"$pw_build_meson" "$trace_lib" --no-doxygen >/dev/null
prepend_project_header "$trace_lib"
meson setup "$trace_lib/build" "$trace_lib" >/dev/null
CCACHE_DISABLE=1 meson compile -C "$trace_lib/build" >/dev/null
cp "$trace_lib/build/libtracelib.so" "$trace_prefix/lib/libtracelib.so"
strip --strip-debug "$trace_prefix/lib/libtracelib.so"
if ! readelf -SW "$trace_prefix/lib/libtracelib.so" | grep -Fq '.phpef_stacktrace'; then
    echo 'stripped external trace library lost .phpef_stacktrace section' >&2
    readelf -SW "$trace_prefix/lib/libtracelib.so" >&2
    exit 1
fi
if readelf -SW "$trace_prefix/lib/libtracelib.so" | grep -Fq '.debug_phpef'; then
    echo 'stripped external trace library still contains debug-only .debug_phpef section' >&2
    readelf -SW "$trace_prefix/lib/libtracelib.so" >&2
    exit 1
fi
cp "$trace_lib/build/tracelib.vapi" "$trace_prefix/share/vala/vapi/tracelib.vapi"
cp "$trace_lib/build/tracelib.deps" "$trace_prefix/share/vala/vapi/tracelib.deps"
cp "$trace_lib/build/tracelib.php" "$trace_prefix/share/phpef/libstubs/tracelib.php"
cp "$trace_lib/build/tracelib.h" "$trace_prefix/include/tracelib/tracelib.h"
cat > "$trace_prefix/lib/pkgconfig/tracelib.pc" <<PC
prefix=$trace_prefix
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
vapidir=\${prefix}/share/vala/vapi

Name: tracelib
Description: PHPEF smoke external stripped stacktrace library
Version: 0.1
Requires: phpef-runtime-core phpef-runtime-embed
Libs: -L\${libdir} -ltracelib
Cflags: -I\${includedir}/tracelib
PC
cat > "$trace_app/main.php" <<'PHP'
<?php
tracelib_boom();
PHP
cat > "$trace_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (tracelib)
PW
PKG_CONFIG_PATH="$trace_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$trace_prefix/share/phpef/libstubs" "$pw_build_meson" "$trace_app" --no-doxygen >/dev/null
prepend_project_header "$trace_app"
PKG_CONFIG_PATH="$trace_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$trace_app/build" "$trace_app" >/dev/null
PKG_CONFIG_PATH="$trace_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$trace_app/build" >/dev/null
set +e
LD_LIBRARY_PATH="$trace_prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$trace_app/build/app" > "$trace_app/app.out" 2>&1
trace_rc=$?
set -e
if [ "$trace_rc" -eq 0 ]; then
    echo 'expected external stripped trace app to fail with UnexpectedValueException' >&2
    cat "$trace_app/app.out" >&2
    exit 1
fi
if ! grep -Fq 'trace-lib-boom' "$trace_app/app.out"; then
    echo 'expected thrown exception message from stripped external trace library' >&2
    cat "$trace_app/app.out" >&2
    exit 1
fi
if ! grep -Fq "in 'tracelib_boom' at $trace_lib/lib/main.php:3" "$trace_app/app.out"; then
    echo 'expected runtime stacktrace to resolve PHP function frame name and source line from stripped external library' >&2
    cat "$trace_app/app.out" >&2
    exit 1
fi
if grep -Fq 'phpef_' "$trace_app/app.out"; then
    echo 'runtime stacktrace leaked generated PHPEF symbol name for stripped external library' >&2
    cat "$trace_app/app.out" >&2
    exit 1
fi
printf 'ok 3 - stripped external PHPEF .so keeps runtime PHP stacktrace frame via .phpef_stacktrace\n'

install_phpef_library() {
    local lib_build=$1
    local libname=$2
    local lib_prefix=$3
    mkdir -p "$lib_prefix/lib/pkgconfig" "$lib_prefix/share/vala/vapi" "$lib_prefix/share/phpef/libstubs" "$lib_prefix/include/$libname"
    cp "$lib_build/build/lib$libname.so" "$lib_prefix/lib/lib$libname.so"
    cp "$lib_build/build/$libname.vapi" "$lib_prefix/share/vala/vapi/$libname.vapi"
    cp "$lib_build/build/$libname.deps" "$lib_prefix/share/vala/vapi/$libname.deps"
    cp "$lib_build/build/$libname.php" "$lib_prefix/share/phpef/libstubs/$libname.php"
    cp "$lib_build/build/$libname.h" "$lib_prefix/include/$libname/$libname.h"
    cat > "$lib_prefix/lib/pkgconfig/$libname.pc" <<PC
prefix=$lib_prefix
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
vapidir=\${prefix}/share/vala/vapi

Name: $libname
Description: PHPEF smoke external library $libname
Version: 0.1
Requires: phpef-runtime-core phpef-runtime-embed
Libs: -L\${libdir} -l$libname
Cflags: -I\${includedir}/$libname
PC
}

# External class/method smoke: a library-provided class method must be visible
# through generated stubs/VAPI, not just top-level functions.
class_lib="$work/class-lib"
class_app="$work/class-app"
class_prefix="$work/class-prefix"
mkdir -p "$class_lib/lib" "$class_app"
cat > "$class_lib/lib/main.php" <<'PHP'
<?php
class ClassLibProbe {
    public static function answer() {
        print("class-method\n");
    }
}
PHP
cat > "$class_lib/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
elib = lib/main.php -> classlib
PW
"$pw_build_meson" "$class_lib" --no-doxygen >/dev/null
prepend_project_header "$class_lib"
meson setup "$class_lib/build" "$class_lib" >/dev/null
CCACHE_DISABLE=1 meson compile -C "$class_lib/build" >/dev/null
install_phpef_library "$class_lib" classlib "$class_prefix"
cat > "$class_app/main.php" <<'PHP'
<?php
ClassLibProbe::answer();
PHP
cat > "$class_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (classlib)
PW
PKG_CONFIG_PATH="$class_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$class_prefix/share/phpef/libstubs" "$pw_build_meson" "$class_app" --no-doxygen >/dev/null
prepend_project_header "$class_app"
PKG_CONFIG_PATH="$class_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$class_app/build" "$class_app" >/dev/null
PKG_CONFIG_PATH="$class_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$class_app/build" >/dev/null
LD_LIBRARY_PATH="$class_prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" run_app_and_check "$class_app/build/app" 'class-method' "$class_app/app.out"
printf 'ok 4 - external PHPEF .so library class static method builds and runs\n'

# Multi-source library smoke: non-entry library files are part of the known
# source set, but become PHP-visible through normal include/require activation
# inside the library unit. Linking an ebin to the elib should not require the
# app to include library internals itself.
multi_lib="$work/multi-lib"
multi_app="$work/multi-app"
multi_prefix="$work/multi-prefix"
mkdir -p "$multi_lib/lib" "$multi_app"
cat > "$multi_lib/lib/main.php" <<'PHP'
<?php
include "helper.php";
print("multi-top\n");
function multilib_answer() {
    multilib_helper();
}
PHP
cat > "$multi_lib/lib/helper.php" <<'PHP'
<?php
function multilib_helper() {
    print("multi-helper\n");
}
PHP
cat > "$multi_lib/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
elib = lib/main.php, lib/helper.php -> multilib
PW
"$pw_build_meson" "$multi_lib" --no-doxygen >/dev/null
prepend_project_header "$multi_lib"
meson setup "$multi_lib/build" "$multi_lib" >/dev/null
CCACHE_DISABLE=1 meson compile -C "$multi_lib/build" >/dev/null
install_phpef_library "$multi_lib" multilib "$multi_prefix"
cat > "$multi_app/main.php" <<'PHP'
<?php
print("app\n");
multilib_answer();
PHP
cat > "$multi_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (multilib)
PW
PKG_CONFIG_PATH="$multi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$multi_prefix/share/phpef/libstubs" "$pw_build_meson" "$multi_app" --no-doxygen >/dev/null
prepend_project_header "$multi_app"
PKG_CONFIG_PATH="$multi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$multi_app/build" "$multi_app" >/dev/null
PKG_CONFIG_PATH="$multi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$multi_app/build" >/dev/null
LD_LIBRARY_PATH="$multi_prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" run_app_and_check "$multi_app/build/app" $'multi-top\napp\nmulti-helper' "$multi_app/app.out"
grep -Fq 'phpef_file_lib_helper_php();' "$multi_lib/build/multilib.vala"
printf 'ok 5 - external multi-source PHPEF .so activates non-entry source functions via include\n'

# Negative external dependency diagnostics: missing stub, missing VAPI, and a
# stale/mismatched stub should fail predictably instead of silently producing a
# wrong binary.
missing_stub="$work/missing-stub-app"
missing_prefix="$work/missing-prefix"
mkdir -p "$missing_stub" "$missing_prefix/lib/pkgconfig" "$missing_prefix/share/vala/vapi" "$missing_prefix/include/missinglib"
cat > "$missing_stub/main.php" <<'PHP'
<?php
missinglib_answer();
PHP
cat > "$missing_stub/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (missinglib)
PW
cat > "$missing_prefix/share/vala/vapi/missinglib.vapi" <<'VAPI'
[CCode (cheader_filename = "missinglib.h")]
public static void phpef_library_missinglib_init () throws GLib.Error;
VAPI
: > "$missing_prefix/share/vala/vapi/missinglib.deps"
touch "$missing_prefix/include/missinglib/missinglib.h"
cat > "$missing_prefix/lib/pkgconfig/missinglib.pc" <<PC
prefix=$missing_prefix
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
vapidir=\${prefix}/share/vala/vapi

Name: missinglib
Description: PHPEF smoke missing stub library
Version: 0.1
Requires: phpef-runtime-core phpef-runtime-embed
Libs: -L\${libdir} -lmissinglib
Cflags: -I\${includedir}/missinglib
PC
PKG_CONFIG_PATH="$missing_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$missing_prefix/share/phpef/libstubs" "$pw_build_meson" "$missing_stub" --no-doxygen >/dev/null
prepend_project_header "$missing_stub"
set +e
PKG_CONFIG_PATH="$missing_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$missing_stub/build" "$missing_stub" > "$missing_stub/setup.out" 2>&1
missing_stub_rc=$?
set -e
if [ "$missing_stub_rc" -eq 0 ] || ! grep -Fq 'missinglib.php does not exist' "$missing_stub/setup.out"; then
    echo 'expected missing external PHPEF stub diagnostic' >&2
    cat "$missing_stub/setup.out" >&2
    exit 1
fi

missing_vapi_app="$work/missing-vapi-app"
missing_vapi_prefix="$work/missing-vapi-prefix"
mkdir -p "$missing_vapi_app" "$missing_vapi_prefix/lib/pkgconfig" "$missing_vapi_prefix/share/vala/vapi" "$missing_vapi_prefix/share/phpef/libstubs" "$missing_vapi_prefix/include/novapilib"
cat > "$missing_vapi_app/main.php" <<'PHP'
<?php
novapilib_answer();
PHP
cat > "$missing_vapi_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (novapilib)
PW
cat > "$missing_vapi_prefix/share/phpef/libstubs/novapilib.php" <<'PHP'
<?php
function novapilib_answer() {}
PHP
touch "$missing_vapi_prefix/include/novapilib/novapilib.h" "$missing_vapi_prefix/lib/libnovapilib.so"
cat > "$missing_vapi_prefix/lib/pkgconfig/novapilib.pc" <<PC
prefix=$missing_vapi_prefix
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include
vapidir=\${prefix}/share/vala/vapi

Name: novapilib
Description: PHPEF smoke missing VAPI library
Version: 0.1
Requires: phpef-runtime-core phpef-runtime-embed
Libs: -L\${libdir} -lnovapilib
Cflags: -I\${includedir}/novapilib
PC
PKG_CONFIG_PATH="$missing_vapi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$missing_vapi_prefix/share/phpef/libstubs" "$pw_build_meson" "$missing_vapi_app" --no-doxygen >/dev/null
prepend_project_header "$missing_vapi_app"
PKG_CONFIG_PATH="$missing_vapi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$missing_vapi_app/build" "$missing_vapi_app" >/dev/null
set +e
PKG_CONFIG_PATH="$missing_vapi_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$missing_vapi_app/build" > "$missing_vapi_app/compile.out" 2>&1
missing_vapi_rc=$?
set -e
if [ "$missing_vapi_rc" -eq 0 ] || ! grep -Eq "Package .*novapilib.* not found|novapilib.*not found" "$missing_vapi_app/compile.out"; then
    echo 'expected missing external PHPEF VAPI diagnostic' >&2
    cat "$missing_vapi_app/compile.out" >&2
    exit 1
fi

mismatch_app="$work/mismatch-stub-app"
mismatch_prefix="$work/mismatch-prefix"
mkdir -p "$mismatch_app"
cp -a "$prefix" "$mismatch_prefix"
cat > "$mismatch_prefix/share/phpef/libstubs/extlib.php" <<'PHP'
<?php
function extlib_missing() {}
PHP
cat > "$mismatch_app/main.php" <<'PHP'
<?php
extlib_missing();
PHP
cat > "$mismatch_app/meson.build.pw" <<'PW'
phpef-runtime-embed = true
phpef-runtime-natives = standard
ebin = main.php -> app (extlib)
PW
PKG_CONFIG_PATH="$mismatch_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" PHPEF_LIBSTUBS_DIR="$mismatch_prefix/share/phpef/libstubs" "$pw_build_meson" "$mismatch_app" --no-doxygen >/dev/null
prepend_project_header "$mismatch_app"
PKG_CONFIG_PATH="$mismatch_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" meson setup "$mismatch_app/build" "$mismatch_app" >/dev/null
PKG_CONFIG_PATH="$mismatch_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" CCACHE_DISABLE=1 meson compile -C "$mismatch_app/build" >/dev/null
set +e
LD_LIBRARY_PATH="$mismatch_prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$mismatch_app/build/app" > "$mismatch_app/app.out" 2>&1
mismatch_rc=$?
set -e
if [ "$mismatch_rc" -eq 0 ] || ! grep -Fq 'Call to undefined function extlib_missing()' "$mismatch_app/app.out"; then
    echo 'expected stale external PHPEF stub to fail at runtime with undefined function' >&2
    cat "$mismatch_app/app.out" >&2
    exit 1
fi
printf 'ok 6 - external PHPEF negative dependency diagnostics cover missing stub, missing VAPI, and stale stub\n'

# Runtime policy smoke: phpef-runtime-embed=false should fail with a PHPEF
# policy diagnostic when generated code would still need the embed fallback.
noembed_app="$work/noembed-app"
mkdir -p "$noembed_app"
cat > "$noembed_app/main.php" <<'PHP'
<?php
print("needs embed\n");
PHP
cat > "$noembed_app/meson.build.pw" <<'PW'
phpef-runtime-embed = false
phpef-runtime-natives = standard
ebin = main.php -> app
PW
"$pw_build_meson" "$noembed_app" --no-doxygen >/dev/null
prepend_project_header "$noembed_app"
meson setup "$noembed_app/build" "$noembed_app" >/dev/null
set +e
CCACHE_DISABLE=1 meson compile -C "$noembed_app/build" > "$noembed_app/compile.out" 2>&1
noembed_rc=$?
set -e
if [ "$noembed_rc" -eq 0 ] || ! grep -Fq 'PHPEF target requires phpef-runtime-embed, but meson.build.pw has phpef-runtime-embed=false' "$noembed_app/compile.out"; then
    echo 'expected phpef-runtime-embed=false policy diagnostic' >&2
    cat "$noembed_app/compile.out" >&2
    exit 1
fi
printf 'ok 7 - phpef-runtime-embed=false fails with explicit policy diagnostic when embed fallback remains\n'
