#include "src/phpef_runtime_embed_value_conversion.h"
#include "phpef-runtime-embed.h"

#include <Zend/zend_exceptions.h>
#include <ext/spl/spl_exceptions.h>
#include <glib.h>
#include <php.h>
#include <string.h>

static void
assert_string_property(zend_class_entry *scope, zval *object, const char *name, const char *expected)
{
    zval rv;
    zval *property = zend_read_property(scope, Z_OBJ_P(object), name, strlen(name), 0, &rv);
    g_assert_nonnull(property);
    g_assert_cmpint(Z_TYPE_P(property), ==, IS_STRING);
    g_assert_cmpstr(Z_STRVAL_P(property), ==, expected);
}

static void
assert_long_property(zend_class_entry *scope, zval *object, const char *name, zend_long expected)
{
    zval rv;
    zval *property = zend_read_property(scope, Z_OBJ_P(object), name, strlen(name), 0, &rv);
    g_assert_nonnull(property);
    g_assert_cmpint(Z_TYPE_P(property), ==, IS_LONG);
    g_assert_cmpint(Z_LVAL_P(property), ==, expected);
}

int
main(int argc, char **argv)
{
    phpef_runtime_embed_engine_start(argv, argc);

    PhpefRuntimeCoreZval *previous_message = phpef_runtime_core_zval_from_string("root");
    PhpefRuntimeCoreZval *previous_code = phpef_runtime_core_zval_from_int(7);
    PhpefRuntimeCoreZval *previous = phpef_runtime_core_create_throwable_zval(
        "RuntimeException",
        previous_message,
        previous_code,
        "previous.php",
        11,
        NULL,
        NULL
    );

    PhpefRuntimeCoreZval *message = phpef_runtime_core_zval_from_string("wrapped");
    PhpefRuntimeCoreZval *code = phpef_runtime_core_zval_from_int(42);
    PhpefRuntimeCoreZval *throwable = phpef_runtime_core_create_throwable_zval(
        "UnexpectedValueException",
        message,
        code,
        "outer.php",
        23,
        previous,
        NULL
    );

    zval converted;
    phpef_runtime_embed_phpefzval_to_zendzval(throwable, &converted);

    g_assert_cmpint(Z_TYPE(converted), ==, IS_OBJECT);
    g_assert_true(instanceof_function(Z_OBJCE(converted), zend_ce_throwable));
    g_assert_true(spl_ce_UnexpectedValueException == NULL || instanceof_function(Z_OBJCE(converted), spl_ce_UnexpectedValueException));

    zend_class_entry *property_scope = zend_get_exception_base(Z_OBJ_P(&converted));
    g_assert_nonnull(property_scope);
    assert_string_property(property_scope, &converted, "message", "wrapped");
    assert_long_property(property_scope, &converted, "code", 42);
    assert_string_property(property_scope, &converted, "file", "outer.php");
    assert_long_property(property_scope, &converted, "line", 23);

    zval rv;
    zval *previous_property = zend_read_property(property_scope, Z_OBJ_P(&converted), "previous", sizeof("previous") - 1, 0, &rv);
    g_assert_nonnull(previous_property);
    g_assert_cmpint(Z_TYPE_P(previous_property), ==, IS_OBJECT);
    g_assert_true(instanceof_function(Z_OBJCE_P(previous_property), zend_ce_throwable));

    zval_ptr_dtor(&converted);
    g_object_unref(throwable);
    g_object_unref(code);
    g_object_unref(message);
    g_object_unref(previous);
    g_object_unref(previous_code);
    g_object_unref(previous_message);
    phpef_runtime_embed_engine_stop();
    return 0;
}
