#ifndef NIF_UTILS_HPP
#define NIF_UTILS_HPP

#include <erl_nif.h>
#include <string>

namespace erlang::nif {

// Get a UTF-8 string from an Elixir binary term
inline bool get_binary_string(ErlNifEnv *env, ERL_NIF_TERM term, std::string &out) {
    ErlNifBinary bin;
    if (!enif_inspect_binary(env, term, &bin)) {
        return false;
    }
    out.assign(reinterpret_cast<const char *>(bin.data), bin.size);
    return true;
}

// Create an Elixir binary term from a std::string
inline ERL_NIF_TERM make_binary(ErlNifEnv *env, const std::string &str) {
    ERL_NIF_TERM term;
    unsigned char *data = enif_make_new_binary(env, str.size(), &term);
    memcpy(data, str.data(), str.size());
    return term;
}

inline ERL_NIF_TERM make_binary(ErlNifEnv *env, const char *str) {
    return make_binary(env, std::string(str ? str : ""));
}

inline ERL_NIF_TERM ok(ErlNifEnv *env) {
    return enif_make_atom(env, "ok");
}

inline ERL_NIF_TERM error(ErlNifEnv *env) {
    return enif_make_atom(env, "error");
}

inline ERL_NIF_TERM ok(ErlNifEnv *env, ERL_NIF_TERM value) {
    return enif_make_tuple2(env, ok(env), value);
}

inline ERL_NIF_TERM error(ErlNifEnv *env, ERL_NIF_TERM reason) {
    return enif_make_tuple2(env, error(env), reason);
}

inline ERL_NIF_TERM error(ErlNifEnv *env, const char *reason) {
    return error(env, enif_make_atom(env, reason));
}

} // namespace erlang::nif

// Template for type-safe NIF resources
template<typename T>
struct NifRes {
    static ErlNifResourceType* type;

    T* val;  // The actual C++ object

    static NifRes<T>* allocate(ErlNifEnv* env) {
        (void)env;
        void* ptr = enif_alloc_resource(type, sizeof(NifRes<T>));
        if (!ptr) return nullptr;
        NifRes<T>* res = new (ptr) NifRes<T>();
        res->val = nullptr;
        return res;
    }

    static NifRes<T>* get(ErlNifEnv* env, ERL_NIF_TERM term) {
        NifRes<T>* res = nullptr;
        enif_get_resource(env, term, type, (void**)&res);
        return res;
    }
};

#endif // NIF_UTILS_HPP
