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

build_root=$1
cli_dir="$build_root/src/cli"
php2ast="$cli_dir/phpefc-php2ast"
phpast2phpir="$cli_dir/phpefc-phpast2phpir"
phpir2valair="$cli_dir/phpefc-phpir2valair"
valair_optimize="$cli_dir/phpefc-valair-optimize-fastpath-runtime"
valair2vala="$cli_dir/phpefc-valair2vala"

for tool in "$php2ast" "$phpast2phpir" "$phpir2valair" "$valair_optimize" "$valair2vala"; do
    if [ ! -x "$tool" ]; then
        echo "missing built tool: $tool" >&2
        exit 99
    fi
done

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

src="$work/fastpath-runtime-self-dispatch.php"
phpast="$work/fastpath-runtime-self-dispatch.phpast"
phpir="$work/fastpath-runtime-self-dispatch.phpir"
valair="$work/fastpath-runtime-self-dispatch.valair"
afteropt="$work/fastpath-runtime-self-dispatch.valair.afteropt.fastpath-runtime"
vala="$work/fastpath-runtime-self-dispatch.vala"
stderr_file="$work/fastpath-runtime-self-dispatch.stderr"

cat >"$src" <<'PHP'
<?php
class FastPathRuntimeProbe {
    private function inc($n) {
        return $n + 1;
    }

    private function twice($n) {
        return $this->inc($n) + $this->inc($n);
    }

    private function ping() {
        return 1;
    }

    private function dispatch($name) {
        $this->$name();
    }

    public static function run() {
        $obj = new FastPathRuntimeProbe();
        return $obj->twice(1);
    }

    public static function runDynamic() {
        $obj = new FastPathRuntimeProbe();
        $obj->dispatch("ping");
    }
}
FastPathRuntimeProbe::run();
FastPathRuntimeProbe::runDynamic();
PHP

"$php2ast" "$src" "$phpast" >/dev/null
"$phpast2phpir" "$phpast" "$phpir" >/dev/null
"$phpir2valair" "$phpir" "$valair" >/dev/null
if ! "$valair_optimize" "$valair" "$afteropt" >/dev/null 2>"$stderr_file"; then
    echo "fastpath-runtime optimizer failed" >&2
    cat "$stderr_file" >&2
    exit 1
fi
"$valair2vala" "$afteropt" "$vala" >/dev/null

if ! grep -Fq 'inc__phpefc_int(n)' "$vala"; then
    echo 'expected direct self-dispatch call to generated/native int inc helper' >&2
    cat "$vala" >&2
    exit 1
fi

if grep -Fq 'PhpefRuntimeCore.call_instance_method(__phpefc_this, "inc"' "$vala"; then
    echo 'self-dispatch RuntimeCore call was not rewritten' >&2
    cat "$vala" >&2
    exit 1
fi

if ! grep -Fq 'twice(obj, new Zval[] {Zval.from_int(1)})' "$vala"; then
    echo 'expected direct dispatch through known local new_object variable' >&2
    cat "$vala" >&2
    exit 1
fi

if grep -Fq 'PhpefRuntimeCore.call_instance_method(obj, "twice"' "$vala"; then
    echo 'known local object RuntimeCore call was not rewritten' >&2
    cat "$vala" >&2
    exit 1
fi

if ! grep -Fq 'if (name.identical(Zval.from_string("ping")).to_bool()) {' "$vala"; then
    echo 'expected dynamic self-dispatch branch for known method name' >&2
    cat "$vala" >&2
    exit 1
fi

if ! grep -Fq 'ping(__phpefc_this, new Zval[] {});' "$vala"; then
    echo 'expected direct call inside dispatch dynamic self method branch' >&2
    cat "$vala" >&2
    exit 1
fi

if ! grep -Fq 'PhpefRuntimeCore.throw_exception("Error", Zval.from_string("Call to undefined method FastPathRuntimeProbe::").concat(name).concat(Zval.from_string("()")));' "$vala"; then
    echo 'expected direct unknown-method throw fallback for dynamic method name' >&2
    cat "$vala" >&2
    exit 1
fi

if grep -Fq 'PhpefRuntimeCore.call_instance_method(__phpefc_this, name.to_php_string(), new Zval[] {}, "FastPathRuntimeProbe");' "$vala"; then
    echo 'unexpected RuntimeCore dispatch fallback for optimized dynamic method name' >&2
    cat "$vala" >&2
    exit 1
fi

if ! grep -Fq '// phpefc optimizer fastpath-runtime result: 2/11 runtime dispatch calls left' "$vala"; then
    echo 'missing/incorrect fastpath-runtime marker' >&2
    cat "$vala" >&2
    exit 1
fi


external_support_src="$work/fastpath-runtime-external-support.php"
external_main_src="$work/fastpath-runtime-external-main.php"
external_support_phpast="$work/fastpath-runtime-external-support.phpast"
external_main_phpast="$work/fastpath-runtime-external-main.phpast"
external_support_phpir="$work/fastpath-runtime-external-support.phpir"
external_main_phpir="$work/fastpath-runtime-external-main.phpir"
external_support_valair="$work/fastpath-runtime-external-support.valair"
external_main_valair="$work/fastpath-runtime-external-main.valair"
external_afteropt="$work/fastpath-runtime-external-main.valair.afteropt.fastpath-runtime"
external_vala="$work/fastpath-runtime-external-main.vala"
external_stderr_file="$work/fastpath-runtime-external.stderr"

printf "%s\n" "<?php" "class ExternalFastPathRuntimeProbe {" "    public static function run() {" "        echo \"external\";" "    }" "}" >"$external_support_src"
printf "%s\n" "<?php" "include \"fastpath-runtime-external-support.php\";" "ExternalFastPathRuntimeProbe::run();" >"$external_main_src"

"$php2ast" "$external_support_src" "$external_support_phpast" >/dev/null
"$php2ast" "$external_main_src" "$external_main_phpast" >/dev/null
"$phpast2phpir" "$external_support_phpast" "$external_support_phpir" --external-phpast "$external_main_phpast" >/dev/null
"$phpast2phpir" "$external_main_phpast" "$external_main_phpir" --external-phpast "$external_support_phpast" >/dev/null
"$phpir2valair" "$external_support_phpir" "$external_support_valair" --function-prefix fastpath_runtime_external_support_php >/dev/null
"$phpir2valair" "$external_main_phpir" "$external_main_valair" --external-phpir "$external_support_phpir" --function-prefix fastpath_runtime_external_main_php --include-function fastpath-runtime-external-support.php phpef_file_fastpath_runtime_external_support_php --source-function-prefix fastpath-runtime-external-support.php fastpath_runtime_external_support_php >/dev/null
if ! "$valair_optimize" "$external_main_valair" "$external_afteropt" --external-valair "$external_support_valair" >/dev/null 2>"$external_stderr_file"; then
    echo "fastpath-runtime optimizer failed for external ValaIR context" >&2
    cat "$external_stderr_file" >&2
    exit 1
fi
"$valair2vala" "$external_afteropt" "$external_vala" >/dev/null

if grep -Fq "PhpefRuntimeCore.call_static_method(\"ExternalFastPathRuntimeProbe\", \"run\"" "$external_vala"; then
    echo "external include-proven static RuntimeCore call was not rewritten" >&2
    cat "$external_vala" >&2
    exit 1
fi

if ! grep -Fq "phpef_fastpath_runtime_external_support_php__ExternalFastPathRuntimeProbe.run();" "$external_vala"; then
    echo "expected direct static call to included support-file class method" >&2
    cat "$external_vala" >&2
    exit 1
fi

if ! grep -Fq "// phpefc optimizer fastpath-runtime result: 0/1 runtime dispatch calls left" "$external_vala"; then
    echo "missing/incorrect external fastpath-runtime marker" >&2
    cat "$external_vala" >&2
    exit 1
fi


function_entry_src="$work/fastpath-runtime-function-entry.php"
function_lib_src="$work/fastpath-runtime-function-lib.php"
function_entry_phpast="$work/fastpath-runtime-function-entry.phpast"
function_lib_phpast="$work/fastpath-runtime-function-lib.phpast"
function_entry_phpir="$work/fastpath-runtime-function-entry.phpir"
function_lib_phpir="$work/fastpath-runtime-function-lib.phpir"
function_entry_valair="$work/fastpath-runtime-function-entry.valair"
function_afteropt="$work/fastpath-runtime-function-entry.valair.afteropt.fastpath-runtime"
function_vala="$work/fastpath-runtime-function-entry.vala"
function_stderr_file="$work/fastpath-runtime-function.stderr"

printf "%s\n" "<?php" "included_func();" >"$function_entry_src"
printf "%s\n" "<?php" "function included_func() {}" >"$function_lib_src"

"$php2ast" "$function_entry_src" "$function_entry_phpast" >/dev/null
"$php2ast" "$function_lib_src" "$function_lib_phpast" >/dev/null
"$phpast2phpir" "$function_entry_phpast" "$function_entry_phpir" --external-phpast "$function_lib_phpast" >/dev/null
"$phpast2phpir" "$function_lib_phpast" "$function_lib_phpir" --external-phpast "$function_entry_phpast" >/dev/null
"$phpir2valair" "$function_entry_phpir" "$function_entry_valair" --external-phpir "$function_lib_phpir" >/dev/null
if ! "$valair_optimize" "$function_entry_valair" "$function_afteropt" >/dev/null 2>"$function_stderr_file"; then
    echo "fastpath-runtime optimizer failed for function dispatch count" >&2
    cat "$function_stderr_file" >&2
    exit 1
fi
"$valair2vala" "$function_afteropt" "$function_vala" >/dev/null

if ! grep -Fq "PhpefRuntimeCore.call_function(\"included_func\", new Zval[] {});" "$function_vala"; then
    echo "expected unresolved external function call to remain a RuntimeCore function dispatch" >&2
    cat "$function_vala" >&2
    exit 1
fi

if ! grep -Fq "// phpefc optimizer fastpath-runtime result: 1/1 runtime dispatch calls left" "$function_vala"; then
    echo "missing/incorrect fastpath-runtime marker for call_function dispatch" >&2
    cat "$function_vala" >&2
    exit 1
fi

printf 'ok - fastpath-runtime rewrites literal, known-object, and dynamic self dispatch fast paths\n'
