// Pure C NIF for net-snmp MIB resolution
// Directly links to libnetsnmp without Rust layer

// Enable BSD types (u_char, u_short, etc.) on newer glibc
#define _DEFAULT_SOURCE
#define _BSD_SOURCE

#include <erl_nif.h>
#include <sys/types.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

// Initialization state
static int snmp_initialized = 0;

// Helper function to create Elixir string (binary) from C string
static ERL_NIF_TERM make_binary_string(ErlNifEnv* env, const char* str) {
    size_t len = strlen(str);
    ErlNifBinary bin;
    if (!enif_alloc_binary(len, &bin)) {
        return enif_make_badarg(env);
    }
    memcpy(bin.data, str, len);
    return enif_make_binary(env, &bin);
}

// Initialize net-snmp library
static ERL_NIF_TERM init_mib_library(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    if (snmp_initialized) {
        return make_binary_string(env, "already_initialized");
    }

    // Initialize SNMP library
    // net-snmp reads MIB directories from MIBDIRS and MIBS environment variables
    // These must be set BEFORE calling this function
    init_snmp("towerops");

    // Initialize MIB subsystem
    netsnmp_init_mib();

    // Load all MIBs into memory
    // This takes ~1-2 seconds but only happens once
    read_all_mibs();

    snmp_initialized = 1;

    return make_binary_string(env, "initialized");
}

// Set MIB directory - should be called BEFORE init_mib_library
static ERL_NIF_TERM load_mib_directory(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifBinary bin;
    char mib_dir[2048];

    // Get binary (Elixir strings are binaries)
    if (!enif_inspect_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    // Copy to null-terminated C string
    if (bin.size >= sizeof(mib_dir)) {
        return enif_make_badarg(env);
    }

    memcpy(mib_dir, bin.data, bin.size);
    mib_dir[bin.size] = '\0';

    // Add MIB directory to search path (can be called before or after init)
    add_mibdir(mib_dir);

    return enif_make_atom(env, "ok");
}

// Resolve MIB name to numeric OID
static ERL_NIF_TERM resolve_oid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifBinary bin;
    char mib_name[256];
    oid objid[MAX_OID_LEN];
    size_t objid_len = MAX_OID_LEN;
    char oid_str[512];

    // Get binary (Elixir strings are binaries)
    if (!enif_inspect_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    // Copy to null-terminated C string
    if (bin.size >= sizeof(mib_name)) {
        return enif_make_badarg(env);
    }

    memcpy(mib_name, bin.data, bin.size);
    mib_name[bin.size] = '\0';

    // Ensure library is initialized
    if (!snmp_initialized) {
        // Auto-initialize on first call
        // net-snmp reads MIB directories from environment variables during init
        // MIBDIRS and MIBS must be set BEFORE this code runs
        init_snmp("towerops");
        netsnmp_init_mib();
        read_all_mibs();
        snmp_initialized = 1;
    }

    // Use snmp_parse_oid which automatically searches all loaded MIBs
    // and handles both "sysDescr" and "SNMPv2-MIB::sysDescr" formats
    if (!snmp_parse_oid(mib_name, objid, &objid_len)) {
        return enif_make_tuple2(env,
            enif_make_atom(env, "error"),
            make_binary_string(env, "Failed to resolve MIB name"));
    }

    // Convert OID array to dotted decimal string (numeric format)
    // Use snprint_objid with -On flag equivalent (numeric only)
    int save_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT);
    netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC);

    if (snprint_objid(oid_str, sizeof(oid_str), objid, objid_len) < 0) {
        netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);
        return enif_make_tuple2(env,
            enif_make_atom(env, "error"),
            make_binary_string(env, "Failed to format OID"));
    }

    // Restore original format setting
    netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);

    // Strip leading dot if present
    char* result = oid_str;
    if (result[0] == '.') {
        result++;
    }

    // Return OID string as binary (Elixir string)
    return make_binary_string(env, result);
}

// NIF function table
static ErlNifFunc nif_funcs[] = {
    {"load_mib_directory", 1, load_mib_directory, 0},
    {"init_mib_library", 0, init_mib_library, ERL_NIF_DIRTY_JOB_CPU_BOUND},
    {"resolve_oid", 1, resolve_oid, ERL_NIF_DIRTY_JOB_CPU_BOUND}
};

// NIF initialization
ERL_NIF_INIT(Elixir.ToweropsNative, nif_funcs, NULL, NULL, NULL, NULL)
