/* niftest.c */
#include <erl_nif.h>
#include <pact.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>

static char *convert_erl_binary_to_c_string(ErlNifEnv *env, ERL_NIF_TERM binary_term)
{
    ErlNifBinary binary;
    char *str;

    // Get the Erlang binary term
    enif_inspect_binary(env, binary_term, &binary);

    // Allocate memory for the string
    str = (char *)enif_alloc(binary.size + 1);

    // Copy the binary data into the string
    memcpy(str, binary.data, binary.size);

    // Add a null-terminating character
    str[binary.size] = '\0';

    return str;
}

static char* extract_json_object(const char *json_string, int object_index) {
    // Extract the nth JSON object from an array
    if (!json_string || json_string[0] != '[') {
        // Not an array, return the whole string
        size_t len = strlen(json_string);
        char *result = enif_alloc(len + 1);
        if (result) {
            strcpy(result, json_string);
        }
        return result;
    }
    
    int current_object = 0;
    int brace_level = 0;
    int in_string = 0;
    int escape_next = 0;
    const char *object_start = NULL;
    
    for (const char *p = json_string; *p; p++) {
        if (escape_next) {
            escape_next = 0;
            continue;
        }
        
        if (*p == '\\') {
            escape_next = 1;
            continue;
        }
        
        if (*p == '"') {
            in_string = !in_string;
            continue;
        }
        
        if (in_string) {
            continue;
        }
        
        if (*p == '{') {
            if (brace_level == 0) {
                // Start of a new object
                if (current_object == object_index) {
                    object_start = p;
                }
                current_object++;
            }
            brace_level++;
        } else if (*p == '}') {
            brace_level--;
            if (brace_level == 0 && object_start && current_object - 1 == object_index) {
                // End of the object we want
                size_t len = p - object_start + 1;
                char *result = enif_alloc(len + 1);
                if (result) {
                    strncpy(result, object_start, len);
                    result[len] = '\0';
                }
                return result;
            }
        }
    }
    
    return NULL;
}

static const char *const * binary_to_char_array(ErlNifEnv* env, const ERL_NIF_TERM binary_term, int object_count) {
    // Convert binary to JSON string(s) for consumer version selectors
    char *json_string = convert_erl_binary_to_c_string(env, binary_term);
    if (!json_string)
        return NULL;

    // Allocate array for JSON strings plus NULL terminator
    const char **char_array = enif_alloc(sizeof(const char *) * (object_count + 1));
    if (!char_array) {
        enif_free(json_string);
        return NULL;
    }

    // Extract each JSON object
    for (int i = 0; i < object_count; i++) {
        char *object_string = extract_json_object(json_string, i);
        if (!object_string) {
            // Cleanup on failure
            for (int j = 0; j < i; j++) {
                enif_free((void*)char_array[j]);
            }
            enif_free(char_array);
            enif_free(json_string);
            return NULL;
        }
        char_array[i] = object_string;
    }
    
    // Set null terminator
    char_array[object_count] = NULL;
    
    // Free the original JSON string as we've extracted individual objects
    enif_free(json_string);
    
    return char_array;
}

static int count_selectors(const char *const *char_array) {
    if (!char_array) return 0;
    
    int count = 0;
    while (char_array[count] != NULL) {
        count++;
    }
    return count;
}

static char* reconstruct_json_array(const char *const *char_array) {
    if (!char_array || !char_array[0]) return NULL;
    
    int count = count_selectors(char_array);
    
    // Always reconstruct as JSON array, even for single object
    size_t total_len = 2; // Start with "[]"
    for (int i = 0; i < count; i++) {
        total_len += strlen(char_array[i]);
        if (i > 0) total_len += 1; // comma separator
    }
    
    char *result = enif_alloc(total_len + 1);
    if (!result) return NULL;
    
    strcpy(result, "[");
    for (int i = 0; i < count; i++) {
        if (i > 0) strcat(result, ",");
        strcat(result, char_array[i]);
    }
    strcat(result, "]");
    
    return result;
}

static void free_char_array(const char *const *char_array) {
    if (char_array) {
        // Free all JSON strings in the array
        for (int i = 0; char_array[i] != NULL; i++) {
            enif_free((void*)char_array[i]);
        }
        // Free the array itself
        enif_free((void*)char_array);
    }
}

static uint8_t* convertCharToUint8(const char* input) {
    // Calculate the length of the string
    size_t length = strlen(input);

    // Allocate memory for the new uint8_t array
    uint8_t* output = (uint8_t*)malloc((length + 1) * sizeof(uint8_t)); // +1 for null terminator
    
    // Copy the data from char* to uint8_t*
    for (size_t i = 0; i < length; i++) {
        output[i] = (uint8_t)input[i];
    }

    // Null-terminate the uint8_t array
    output[length] = '\0';

    return output;
}

static int convert_erl_int_to_c_int(ErlNifEnv *env, ERL_NIF_TERM int_term)
{
    int c_int;
    enif_get_int(env, int_term, &c_int);
    return c_int;
}

static ERL_NIF_TERM version(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_make_string(env, pactffi_version(), ERL_NIF_LATIN1);
}

static ERL_NIF_TERM logger_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    pactffi_logger_init();
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM logger_attach_sink(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_binary(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    char *logPath = convert_erl_binary_to_c_string(env, argv[0]);
    if (!enif_is_number(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    int log_lvl = convert_erl_int_to_c_int(env, argv[1]);
    int result = pactffi_logger_attach_sink(logPath, log_lvl);
    return enif_make_int(env, result);
}

static ERL_NIF_TERM logger_apply(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    pactffi_logger_apply();
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM log_message(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_binary(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *source = convert_erl_binary_to_c_string(env, argv[0]);
    char *log_level = convert_erl_binary_to_c_string(env, argv[1]);
    char *message = convert_erl_binary_to_c_string(env, argv[2]);
    pactffi_log_message(source, log_level, message);

    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM new_pact(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_binary(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *consumer = convert_erl_binary_to_c_string(env, argv[0]);
    char *producer = convert_erl_binary_to_c_string(env, argv[1]);
    PactHandle pacthandle;
    pacthandle = pactffi_new_pact(consumer, producer);

    return enif_make_int(env, pacthandle);
}

static ERL_NIF_TERM new_interaction(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int pact_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *interaction_desc = convert_erl_binary_to_c_string(env, argv[1]);
    PactHandle pacthandle = pact_ref;
    InteractionHandle interactionhandle = pactffi_new_interaction(pacthandle, interaction_desc);

    return enif_make_int(env, interactionhandle);
}

static ERL_NIF_TERM new_msg_interaction(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int pact_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *interaction_desc = convert_erl_binary_to_c_string(env, argv[1]);
    PactHandle pacthandle = pact_ref;
    InteractionHandle interactionhandle = pactffi_new_message_interaction(pacthandle, interaction_desc);

    return enif_make_int(env, interactionhandle);
}

static ERL_NIF_TERM given(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *provider_state = convert_erl_binary_to_c_string(env, argv[1]);
    InteractionHandle interactionhandle = interaction_ref;
    if(pactffi_given(interactionhandle, provider_state))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "provider_state_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_provider_state"));
    }
}

static ERL_NIF_TERM msg_given(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *provider_state = convert_erl_binary_to_c_string(env, argv[1]);
    MessageHandle interactionhandle = interaction_ref;
    pactffi_message_given(interactionhandle, provider_state);
    
    return enif_make_atom(env, "ok"); 
}

static ERL_NIF_TERM given_with_params(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *provider_state = convert_erl_binary_to_c_string(env, argv[1]);
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *params = convert_erl_binary_to_c_string(env, argv[2]);
    InteractionHandle interactionhandle = interaction_ref;
    if(pactffi_given_with_params(interactionhandle, provider_state, params))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "provider_state_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_provider_state"));
    }
}

static ERL_NIF_TERM msg_given_with_param(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *provider_state = convert_erl_binary_to_c_string(env, argv[1]);
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *name = convert_erl_binary_to_c_string(env, argv[2]);
    if (!enif_is_binary(env, argv[3]))
    {
        return enif_make_badarg(env);
    }
    char *value = convert_erl_binary_to_c_string(env, argv[3]);
    MessageHandle interactionhandle = interaction_ref;
    pactffi_message_given_with_param(interactionhandle, provider_state, name, value);
    
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM with_request(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    InteractionHandle interactH = interaction_ref;

    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *request_method = convert_erl_binary_to_c_string(env, argv[1]);
    char *request_path = convert_erl_binary_to_c_string(env, argv[2]);

    if (pactffi_with_request(interactH, request_method, request_path))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "request_path_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_request_path"));
    }
}

static ERL_NIF_TERM with_header_v2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    InteractionHandle interactH = interaction_ref;
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *header_key = convert_erl_binary_to_c_string(env, argv[2]);

    if (!enif_is_number(env, argv[3]))
    {
        return enif_make_badarg(env);
    }
    int header_value_index = convert_erl_int_to_c_int(env, argv[3]);

    if (!enif_is_binary(env, argv[4]))
    {
        return enif_make_badarg(env);
    }
    char *header_value = convert_erl_binary_to_c_string(env, argv[4]);

    if (!enif_is_number(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    int interactionPart = convert_erl_int_to_c_int(env, argv[1]);

    if (pactffi_with_header_v2(interactH, interactionPart, header_key, header_value_index, header_value))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "header_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_header"));
    }
}

static ERL_NIF_TERM with_body(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    InteractionHandle interactH = interaction_ref;

    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[3]))
    {
        return enif_make_badarg(env);
    }
    char *content_type = convert_erl_binary_to_c_string(env, argv[2]);
    char *body_json_string = convert_erl_binary_to_c_string(env, argv[3]);

    if (!enif_is_number(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    int interactionPart = convert_erl_int_to_c_int(env, argv[1]);

    if (pactffi_with_body(interactH, interactionPart, content_type, body_json_string))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "body_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_body"));
    }
}

static ERL_NIF_TERM msg_with_contents(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    MessageHandle interactH = interaction_ref;

    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    if (!enif_is_binary(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    char *content_type = convert_erl_binary_to_c_string(env, argv[1]);
    uint8_t *body_json_string = convertCharToUint8(convert_erl_binary_to_c_string(env, argv[2]));

    pactffi_message_with_contents(interactH, content_type, body_json_string, -1);

    free(body_json_string);

    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM reify_message(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    MessageHandle interactH = interaction_ref;

    const char *reified_message = pactffi_message_reify(interactH);

    if (reified_message != NULL)
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_string(env, reified_message, ERL_NIF_LATIN1));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_reify_message"));
    }
}

static ERL_NIF_TERM response_status(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    InteractionHandle interactH = interaction_ref;
    if (!enif_is_number(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    int response_code = convert_erl_int_to_c_int(env, argv[1]);
    if (pactffi_response_status(interactH, response_code))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "response_status_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_response_status"));
    }
}

static ERL_NIF_TERM create_mock_server_for_transport(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int pact_ref = convert_erl_int_to_c_int(env, argv[0]);
    PactHandle pactH = pact_ref;

    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *address = convert_erl_binary_to_c_string(env, argv[1]);

    if (!enif_is_number(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    int given_port = convert_erl_int_to_c_int(env, argv[2]);

    if (!enif_is_binary(env, argv[3]))
    {
        return enif_make_badarg(env);
    }
    char *transport_type = convert_erl_binary_to_c_string(env, argv[3]);
    int return_port = pactffi_create_mock_server_for_transport(pactH, address, given_port, transport_type, NULL);

    return enif_make_int(env, return_port);
}

static ERL_NIF_TERM mock_server_matched(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int mock_server_port = convert_erl_int_to_c_int(env, argv[0]);
    if (pactffi_mock_server_matched(mock_server_port))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "matched"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "not_matched"));
    }
}

static ERL_NIF_TERM mock_server_mismatches(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int mock_server_port = convert_erl_int_to_c_int(env, argv[0]);
    char *mismatches = pactffi_mock_server_mismatches(mock_server_port);
    if (mismatches != NULL)
    {
        return enif_make_string(env, mismatches, ERL_NIF_LATIN1);
    }
    else
    {
        return enif_make_atom(env, "undefined");
    }
}

static ERL_NIF_TERM pact_handle_write_file(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int pact_ref = convert_erl_int_to_c_int(env, argv[0]);
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *pact_dir = convert_erl_binary_to_c_string(env, argv[1]);
    if (!enif_is_number(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    int overwrite_value = convert_erl_int_to_c_int(env, argv[2]);
    bool overwrite;
    if (overwrite_value == 0)
    {
        overwrite = false;
    }
    else
    {
        overwrite = true;
    }

    return enif_make_int(env, pactffi_pact_handle_write_file(pact_ref, pact_dir, overwrite));
}

static ERL_NIF_TERM log_to_file(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_binary(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    char *file_path = convert_erl_binary_to_c_string(env, argv[0]);
    if (!enif_is_number(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    int log_lvl = convert_erl_int_to_c_int(env, argv[1]);
    pactffi_log_to_file(file_path, log_lvl);

    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM cleanup_mock_server(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int mock_server_port = convert_erl_int_to_c_int(env, argv[0]);
    pactffi_cleanup_mock_server(mock_server_port);
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM free_pact_handle(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    PactHandle pactHandle = convert_erl_int_to_c_int(env, argv[0]);
    pactffi_free_pact_handle(pactHandle);
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM with_query_parameter_v2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    if (!enif_is_number(env, argv[0]))
    {
        return enif_make_badarg(env);
    }
    int interaction_ref = convert_erl_int_to_c_int(env, argv[0]);
    InteractionHandle interactH = interaction_ref;
    if (!enif_is_binary(env, argv[1]))
    {
        return enif_make_badarg(env);
    }
    char *name = convert_erl_binary_to_c_string(env, argv[1]);

    if (!enif_is_number(env, argv[2]))
    {
        return enif_make_badarg(env);
    }
    int index = convert_erl_int_to_c_int(env, argv[2]);

    if (!enif_is_binary(env, argv[3]))
    {
        return enif_make_badarg(env);
    }
    char *value = convert_erl_binary_to_c_string(env, argv[3]);

    if (pactffi_with_query_parameter_v2(interactH, name, index, value))
    {
        return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "query_param_added"));
    }
    else
    {
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "cannot_add_query_param"));
    }
}

// Provider verifier functions

static ERL_NIF_TERM verify_via_file_direct(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    // Extract all parameters
    if (!enif_is_binary(env, argv[0])) return enif_make_badarg(env);
    char *name = convert_erl_binary_to_c_string(env, argv[0]);
    
    if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
    char *scheme = convert_erl_binary_to_c_string(env, argv[1]);
    
    if (!enif_is_binary(env, argv[2])) return enif_make_badarg(env);
    char *host = convert_erl_binary_to_c_string(env, argv[2]);
    
    if (!enif_is_number(env, argv[3])) return enif_make_badarg(env);
    int port = convert_erl_int_to_c_int(env, argv[3]);
    
    if (!enif_is_binary(env, argv[4])) return enif_make_badarg(env);
    char *path = convert_erl_binary_to_c_string(env, argv[4]);
    
    if (!enif_is_binary(env, argv[5])) return enif_make_badarg(env);
    char *version = convert_erl_binary_to_c_string(env, argv[5]);
    
    if (!enif_is_binary(env, argv[6])) return enif_make_badarg(env);
    char *branch = convert_erl_binary_to_c_string(env, argv[6]);
    
    if (!enif_is_binary(env, argv[7])) return enif_make_badarg(env);
    char *file_path = convert_erl_binary_to_c_string(env, argv[7]);
    
    if (!enif_is_binary(env, argv[8])) return enif_make_badarg(env);
    char *protocol = convert_erl_binary_to_c_string(env, argv[8]);
    
    if (!enif_is_binary(env, argv[9])) return enif_make_badarg(env);
    char *state_path = convert_erl_binary_to_c_string(env, argv[9]);

    if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);
    int publish_verification_results = convert_erl_int_to_c_int(env, argv[10]);

    // Create verifier handle and configure it
    struct VerifierHandle *verifierhandle = pactffi_verifier_new_for_application(name, version);
    pactffi_verifier_set_no_pacts_is_error(verifierhandle, 0);
    pactffi_verifier_set_provider_info(verifierhandle, name, scheme, host, port, path);
    pactffi_verifier_add_provider_transport(verifierhandle, protocol, port, path, scheme);
    
    if (state_path[0] != '\0') {
        pactffi_verifier_set_provider_state(verifierhandle, state_path, 0, 1);
    }
    
    pactffi_verifier_set_verification_options(verifierhandle, 0, 5000);
    if (publish_verification_results == 1) {
        pactffi_verifier_set_publish_options(verifierhandle, version, NULL, NULL, -1, branch);
    }
    pactffi_verifier_add_directory_source(verifierhandle, file_path);
    
    setenv("PACT_DO_NOT_TRACK", "true", 1);
    
    // Execute verification
    int output = pactffi_verifier_execute(verifierhandle);
    
    // Cleanup
    pactffi_verifier_shutdown(verifierhandle);
    
    // Free allocated strings
    enif_free(name);
    enif_free(scheme);
    enif_free(host);
    enif_free(path);
    enif_free(version);
    enif_free(branch);
    enif_free(file_path);
    enif_free(protocol);
    enif_free(state_path);

    return enif_make_int(env, output);
}

static ERL_NIF_TERM verify_via_broker_direct(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
    // Extract all parameters (similar to existing verify_via_broker but with proper cleanup)
    if (!enif_is_binary(env, argv[0])) return enif_make_badarg(env);
    char *name = convert_erl_binary_to_c_string(env, argv[0]);
    
    if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
    char *scheme = convert_erl_binary_to_c_string(env, argv[1]);
    
    if (!enif_is_binary(env, argv[2])) return enif_make_badarg(env);
    char *host = convert_erl_binary_to_c_string(env, argv[2]);
    
    if (!enif_is_number(env, argv[3])) return enif_make_badarg(env);
    int port = convert_erl_int_to_c_int(env, argv[3]);
    
    if (!enif_is_binary(env, argv[4])) return enif_make_badarg(env);
    char *path = convert_erl_binary_to_c_string(env, argv[4]);
    
    if (!enif_is_binary(env, argv[5])) return enif_make_badarg(env);
    char *version = convert_erl_binary_to_c_string(env, argv[5]);
    
    if (!enif_is_binary(env, argv[6])) return enif_make_badarg(env);
    char *branch = convert_erl_binary_to_c_string(env, argv[6]);
    
    if (!enif_is_binary(env, argv[7])) return enif_make_badarg(env);
    char *broker_url = convert_erl_binary_to_c_string(env, argv[7]);
    
    if (!enif_is_binary(env, argv[8])) return enif_make_badarg(env);
    char *broker_username = convert_erl_binary_to_c_string(env, argv[8]);
    
    if (!enif_is_binary(env, argv[9])) return enif_make_badarg(env);
    char *broker_password = convert_erl_binary_to_c_string(env, argv[9]);
    
    if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);
    int enable_pending = convert_erl_int_to_c_int(env, argv[10]);
    
    if (!enif_is_number(env, argv[12])) return enif_make_badarg(env);
    int consumer_version_selectors_len = convert_erl_int_to_c_int(env, argv[12]);
    
    if (!enif_is_binary(env, argv[11])) return enif_make_badarg(env);
    const char *const *consumer_version_selectors = binary_to_char_array(env, argv[11], consumer_version_selectors_len);
    
    if (!enif_is_binary(env, argv[13])) return enif_make_badarg(env);
    char *protocol = convert_erl_binary_to_c_string(env, argv[13]);
    
    if (!enif_is_binary(env, argv[14])) return enif_make_badarg(env);
    char *state_path = convert_erl_binary_to_c_string(env, argv[14]);

    if( !enif_is_number(env, argv[15])) return enif_make_badarg(env);
    int publish_verification_results = convert_erl_int_to_c_int(env, argv[15]);

    // Create and configure verifier
    struct VerifierHandle *verifierhandle = pactffi_verifier_new_for_application(name, version);
    pactffi_verifier_set_no_pacts_is_error(verifierhandle, 0);
    pactffi_verifier_set_provider_info(verifierhandle, name, scheme, host, port, path);
    pactffi_verifier_add_provider_transport(verifierhandle, protocol, port, path, scheme);

    if (state_path[0] != '\0') {
        pactffi_verifier_set_provider_state(verifierhandle, state_path, 0, 1);
    }
    
    pactffi_verifier_set_verification_options(verifierhandle, 0, 5000);
    if (publish_verification_results == 1) {
        pactffi_verifier_set_publish_options(verifierhandle, version, NULL, NULL, -1, branch);
    }
    pactffi_verifier_broker_source_with_selectors(verifierhandle, broker_url, broker_username, broker_password, NULL, enable_pending, NULL, NULL, -1, branch, consumer_version_selectors, consumer_version_selectors_len, NULL, -1);
    
    setenv("PACT_DO_NOT_TRACK", "true", 1);

    // Execute verification
    int verification_output = pactffi_verifier_execute(verifierhandle);

    // Cleanup
    pactffi_verifier_shutdown(verifierhandle);
    
    // Free allocated strings
    enif_free(name);
    enif_free(scheme);
    enif_free(host);
    enif_free(path);
    enif_free(version);
    enif_free(branch);
    enif_free(broker_url);
    enif_free(broker_username);
    enif_free(broker_password);
    free_char_array(consumer_version_selectors);
    enif_free(protocol);
    enif_free(state_path);

    return enif_make_int(env, verification_output);
}

static ERL_NIF_TERM verify_via_broker_external(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    // Extract and serialize all arguments as strings
    if (!enif_is_binary(env, argv[0])) return enif_make_badarg(env);
    char *name = convert_erl_binary_to_c_string(env, argv[0]);
    if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
    char *scheme = convert_erl_binary_to_c_string(env, argv[1]);
    if (!enif_is_binary(env, argv[2])) return enif_make_badarg(env);
    char *host = convert_erl_binary_to_c_string(env, argv[2]);
    if (!enif_is_number(env, argv[3])) return enif_make_badarg(env);
    int port = convert_erl_int_to_c_int(env, argv[3]);
    if (!enif_is_binary(env, argv[4])) return enif_make_badarg(env);
    char *path = convert_erl_binary_to_c_string(env, argv[4]);
    if (!enif_is_binary(env, argv[5])) return enif_make_badarg(env);
    char *version = convert_erl_binary_to_c_string(env, argv[5]);
    if (!enif_is_binary(env, argv[6])) return enif_make_badarg(env);
    char *branch = convert_erl_binary_to_c_string(env, argv[6]);
    if (!enif_is_binary(env, argv[7])) return enif_make_badarg(env);
    char *broker_url = convert_erl_binary_to_c_string(env, argv[7]);
    if (!enif_is_binary(env, argv[8])) return enif_make_badarg(env);
    char *broker_username = convert_erl_binary_to_c_string(env, argv[8]);
    if (!enif_is_binary(env, argv[9])) return enif_make_badarg(env);
    char *broker_password = convert_erl_binary_to_c_string(env, argv[9]);
    if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);
    int enable_pending = convert_erl_int_to_c_int(env, argv[10]);
    if (!enif_is_number(env, argv[12])) return enif_make_badarg(env);
    int consumer_version_selectors_len = convert_erl_int_to_c_int(env, argv[12]);
    if (!enif_is_binary(env, argv[11])) return enif_make_badarg(env);
    const char *const *consumer_version_selectors = binary_to_char_array(env, argv[11], consumer_version_selectors_len);
    if (!enif_is_binary(env, argv[13])) return enif_make_badarg(env);
    char *protocol = convert_erl_binary_to_c_string(env, argv[13]);
    if (!enif_is_binary(env, argv[14])) return enif_make_badarg(env);
    char *state_path = convert_erl_binary_to_c_string(env, argv[14]);
    if (!enif_is_number(env, argv[15])) return enif_make_badarg(env);
    int publish_verification_results = convert_erl_int_to_c_int(env, argv[15]);
    if (!enif_is_binary(env, argv[16])) return enif_make_badarg(env);
    char *exec_path = convert_erl_binary_to_c_string(env, argv[16]);

    // Create a temp file for config in the current working directory
    char config_file_template[256];
    snprintf(config_file_template, sizeof(config_file_template), "pact_erlang_config_XXXXXX");
    int config_fd = mkstemp(config_file_template);
    if (config_fd == -1) {
        enif_free(name); enif_free(scheme); enif_free(host); enif_free(path);
        enif_free(version); enif_free(branch); enif_free(broker_url);
        enif_free(broker_username); enif_free(broker_password);
        free_char_array(consumer_version_selectors); enif_free(protocol); enif_free(state_path);
        enif_free(exec_path);
        return enif_make_int(env, -1);
    }

    // Write config as key=value pairs (one per line)
    dprintf(config_fd, "PACT_ERLANG_NAME=%s\n", name);
    dprintf(config_fd, "PACT_ERLANG_SCHEME=%s\n", scheme);
    dprintf(config_fd, "PACT_ERLANG_HOST=%s\n", host);
    dprintf(config_fd, "PACT_ERLANG_PORT=%d\n", port);
    dprintf(config_fd, "PACT_ERLANG_PATH=%s\n", path);
    dprintf(config_fd, "PACT_ERLANG_VERSION=%s\n", version);
    dprintf(config_fd, "PACT_ERLANG_BRANCH=%s\n", branch);
    dprintf(config_fd, "PACT_ERLANG_BROKER_URL=%s\n", broker_url);
    dprintf(config_fd, "PACT_ERLANG_BROKER_USERNAME=%s\n", broker_username);
    dprintf(config_fd, "PACT_ERLANG_BROKER_PASSWORD=%s\n", broker_password);
    dprintf(config_fd, "PACT_ERLANG_ENABLE_PENDING=%d\n", enable_pending);
    dprintf(config_fd, "PACT_ERLANG_CONSUMER_VERSION_SELECTORS=%s\n", reconstruct_json_array(consumer_version_selectors));
    dprintf(config_fd, "PACT_ERLANG_CONSUMER_VERSION_SELECTORS_LEN=%d\n", consumer_version_selectors_len);
    dprintf(config_fd, "PACT_ERLANG_PROTOCOL=%s\n", protocol);
    dprintf(config_fd, "PACT_ERLANG_STATE_PATH=%s\n", state_path);
    dprintf(config_fd, "PACT_ERLANG_PUBLISH_VERIFICATION_RESULTS=%d\n", publish_verification_results);
    close(config_fd);

    // Create a temp file for result in the current working directory
    char result_file_template[256];
    snprintf(result_file_template, sizeof(result_file_template), "pact_erlang_result_XXXXXX");
    int result_fd = mkstemp(result_file_template);
    if (result_fd == -1) {
        unlink(config_file_template);
        enif_free(name); enif_free(scheme); enif_free(host); enif_free(path);
        enif_free(version); enif_free(branch); enif_free(broker_url);
        enif_free(broker_username); enif_free(broker_password);
        free_char_array(consumer_version_selectors); enif_free(protocol); enif_free(state_path);
        enif_free(exec_path);
        return enif_make_int(env, -1);
    }
    close(result_fd);

    // Pass config and result file paths via environment variables
    char *envp[3];
    size_t sz1 = strlen("PACT_ERLANG_CONFIG_FILE=") + strlen(config_file_template) + 1;
    size_t sz2 = strlen("PACT_ERLANG_RESULT_FILE=") + strlen(result_file_template) + 1;
    envp[0] = malloc(sz1);
    envp[1] = malloc(sz2);
    snprintf(envp[0], sz1, "PACT_ERLANG_CONFIG_FILE=%s", config_file_template);
    snprintf(envp[1], sz2, "PACT_ERLANG_RESULT_FILE=%s", result_file_template);
    envp[2] = NULL;

    char *const argv_exec[] = {exec_path, NULL};

    pid_t pid = fork();
    if (pid == 0) {
        execve(exec_path, argv_exec, envp);
        perror("execve failed");
        exit(127);
    } else if (pid > 0) {
        int status;
        waitpid(pid, &status, 0);
        // Read result from file
        int verification_output = -1;
        FILE *fp = fopen(result_file_template, "r");
        if (fp) {
            if (fscanf(fp, "%d", &verification_output) != 1) {
                verification_output = -1;
            }
            fclose(fp);
            unlink(result_file_template);
        }
        unlink(config_file_template);
        free(envp[0]);
        free(envp[1]);
        enif_free(name); enif_free(scheme); enif_free(host); enif_free(path);
        enif_free(version); enif_free(branch); enif_free(broker_url);
        enif_free(broker_username); enif_free(broker_password);
        free_char_array(consumer_version_selectors); enif_free(protocol); enif_free(state_path);
        enif_free(exec_path);
        return enif_make_int(env, verification_output);
    } else {
        free(envp[0]);
        free(envp[1]);
        unlink(config_file_template);
        unlink(result_file_template);
        enif_free(name); enif_free(scheme); enif_free(host); enif_free(path);
        enif_free(version); enif_free(branch); enif_free(broker_url);
        enif_free(broker_username); enif_free(broker_password);
        free_char_array(consumer_version_selectors); enif_free(protocol); enif_free(state_path);
        enif_free(exec_path);
        return enif_make_int(env, -1);
    }
}

static ErlNifFunc nif_funcs[] =
    {
        {"version", 0, version},
        {"logger_init", 0, logger_init},
        {"logger_attach_sink", 2, logger_attach_sink},
        {"logger_apply", 0, logger_apply},
        {"log_message", 3, log_message},
        {"new_pact", 2, new_pact},
        {"new_interaction", 2, new_interaction},
        {"with_request", 3, with_request},
        {"with_header_v2", 5, with_header_v2},
        {"with_body", 4, with_body},
        {"response_status", 2, response_status},
        {"create_mock_server_for_transport", 4, create_mock_server_for_transport},
        {"mock_server_matched", 1, mock_server_matched},
        {"mock_server_mismatches", 1, mock_server_mismatches},
        {"log_to_file", 2, log_to_file},
        {"pact_handle_write_file", 3, pact_handle_write_file},
        {"cleanup_mock_server", 1, cleanup_mock_server},
        {"free_pact_handle", 1, free_pact_handle},
        {"with_query_parameter_v2", 4, with_query_parameter_v2},
        {"given", 2, given},
        {"given_with_params", 3, given_with_params},
        {"new_msg_interaction", 2, new_msg_interaction},
        {"msg_given", 2, msg_given},
        {"msg_given_with_param", 4, msg_given_with_param},
        {"msg_with_contents", 3, msg_with_contents},
        {"reify_message", 1, reify_message},
        {"verify_via_file_direct", 11, verify_via_file_direct, ERL_NIF_DIRTY_JOB_IO_BOUND},
        {"verify_via_broker_direct", 16, verify_via_broker_direct, ERL_NIF_DIRTY_JOB_IO_BOUND},
        {"verify_via_broker_external", 17, verify_via_broker_external, ERL_NIF_DIRTY_JOB_IO_BOUND}
    };

ERL_NIF_INIT(pactffi_nif, nif_funcs, NULL, NULL, NULL, NULL)
