// os_emscripten.c - OS module for Emscripten (Web)
// Provides system information and utilities for the web platform

#include "cell.h"
#include <emscripten.h>
#include <emscripten/heap.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

// cell_ns - nanoseconds since some epoch
uint64_t cell_ns(void)
{
    // emscripten_get_now() returns milliseconds with high precision
    return (uint64_t)(emscripten_get_now() * 1000000.0);
}

// cell_sleep - sleep for specified seconds
void cell_sleep(double seconds)
{
    // Note: This requires ASYNCIFY to work correctly without freezing the browser
    emscripten_sleep((unsigned int)(seconds * 1000));
}

JSC_CCALL(os_now, 
    return number2js(js, cell_ns()); 
)

JSC_CCALL(os_sleep,
    double secs = js2number(js, argv[0]);
    cell_sleep(secs);
)

static JSValue js_os_totalmem(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // Return heap size or a fixed amount?
    // heap size is technically dynamic in wasm, but we can query it.
    // For now, let's return the HEAP8.length equivalent via EM_ASM_INT or similar,
    // or just return a safe default.
    // Better yet, use mallinfo if available.
    return JS_NewInt64(js, (int64_t)emscripten_get_heap_size());
}

static JSValue js_os_platform(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "Emscripten");
}

static JSValue js_os_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "browser");
}

static JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "wasm");
}

static JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
    struct mallinfo i = mallinfo();
    // Approximate free memory based on mallinfo
    // total heap - used
    size_t total = emscripten_get_heap_size();
    size_t used = i.uordblks;
    return JS_NewInt64(js, (int64_t)(total - used));
}

static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // Could return UserAgent, but for now just "web"
    return JS_NewString(js, "web");
}

JSC_CCALL(os_mallinfo,
    ret = JS_NewObject(js);
    struct mallinfo i = mallinfo();
    // Add fields if needed, similar to os.c
    // Using macros from os.c if they were visible, but they aren't public.
    // We'll just define helper locally or expand it.
    JS_SetPropertyStr(js, ret, "arena", JS_NewInt32(js, i.arena));
    JS_SetPropertyStr(js, ret, "ordblks", JS_NewInt32(js, i.ordblks));
    JS_SetPropertyStr(js, ret, "uordblks", JS_NewInt32(js, i.uordblks));
    JS_SetPropertyStr(js, ret, "fordblks", JS_NewInt32(js, i.fordblks));
)

static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewObject(js);
}

JSC_SCALL(os_system,
    // system() not available in browser
    ret = JS_NewInt32(js, -1);
)

JSC_CCALL(os_exit,
    // Terminate execution
    emscripten_force_exit(0);
)

// dylib functions - assuming static build for now unless side modules are used
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_ThrowInternalError(js, "dylib_open: not supported on Emscripten");
}

static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_ThrowInternalError(js, "dylib_symbol: not supported on Emscripten");
}

static JSValue js_os_dylib_has_symbol(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewBool(js, 0);
}

JSC_CCALL(os_print,
    size_t len;
    const char *str = JS_ToCStringLen(js, &len, argv[0]);
    printf("%.*s", (int)len, str);
    JS_FreeCString(js, str);
)

static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // dlsym with NULL usually works in Emscripten MAIN_MODULE builds,
    // but for now we assume no dynamic linking.
    return JS_NULL;
}

static JSValue js_os_internal_exists(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewBool(js, 0);
}

void arc4random_buf(void *buf, size_t nbytes);

// Random number generation
int randombytes(void *buf, size_t n) {
    // Emscripten implements arc4random_buf using Web Crypto API
    arc4random_buf(buf, n);
    return 0;
}

JSC_CCALL(os_random,
    return JS_NewFloat64(js, cell_random_fit());
)

JSC_CCALL(os_getenv,
    const char *name = JS_ToCString(js, argv[0]);
    if (!name) return JS_EXCEPTION;
    const char *value = getenv(name);
    JS_FreeCString(js, name);
    if (value)
        ret = JS_NewString(js, value);
    else
        ret = JS_NULL;
)

static const JSCFunctionListEntry js_os_funcs[] = {
    MIST_FUNC_DEF(os, platform, 0),
    MIST_FUNC_DEF(os, arch, 0),
    MIST_FUNC_DEF(os, totalmem, 0),
    MIST_FUNC_DEF(os, freemem, 0),
    MIST_FUNC_DEF(os, hostname, 0),
    MIST_FUNC_DEF(os, version, 0),
    MIST_FUNC_DEF(os, now, 0),
    MIST_FUNC_DEF(os, rusage, 0),
    MIST_FUNC_DEF(os, mallinfo, 0),
    MIST_FUNC_DEF(os, system, 1),
    MIST_FUNC_DEF(os, exit, 0),
    MIST_FUNC_DEF(os, sleep, 1),
    MIST_FUNC_DEF(os, dylib_open, 1),
    MIST_FUNC_DEF(os, dylib_symbol, 2),
    MIST_FUNC_DEF(os, dylib_has_symbol, 2),
    MIST_FUNC_DEF(os, load_internal, 1),
    MIST_FUNC_DEF(os, internal_exists, 1),
    MIST_FUNC_DEF(os, print, 1),
    MIST_FUNC_DEF(os, random, 0),
    MIST_FUNC_DEF(os, getenv, 1),
};

JSValue js_os_use(JSContext *js) {
    JSValue mod = JS_NewObject(js);
    JS_SetPropertyFunctionList(js, mod, js_os_funcs, countof(js_os_funcs));
    return mod;
}
