#include "cell.h"
#include <string.h>

/* JSON uses JS_KEY_empty for reviver — define locally from public constants */
#ifndef JS_KEY_empty
#define JS_KEY_empty ((JSValue)JS_TAG_STRING_IMM)
#endif

static JSValue js_cell_json_encode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
  if (argc < 1)
    return JS_RaiseDisrupt (ctx, "json.encode requires at least 1 argument");

  int pretty = argc <= 1 || JS_ToBool (ctx, argv[1]);
  JSValue space = pretty ? JS_NewInt32 (ctx, 2) : JS_NULL;
  JSValue replacer = JS_NULL;
  if (argc > 2 && JS_IsFunction (argv[2]))
    replacer = argv[2];
  else if (argc > 3 && JS_IsArray (argv[3]))
    replacer = argv[3];
  JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space, pretty);
  return result;
}

static JSValue js_cell_json_decode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
  if (argc < 1)
    return JS_RaiseDisrupt (ctx, "json.decode requires at least 1 argument");

  if (!JS_IsText (argv[0]))
    return JS_RaiseDisrupt (ctx, "couldn't parse text: not a string");

  const char *str = JS_ToCString (ctx, argv[0]);
  if (!str) return JS_EXCEPTION;

  size_t len = strlen (str);
  JSValue result = JS_ParseJSON (ctx, str, len, "<json>");
  JS_FreeCString (ctx, str);

  /* Apply reviver if provided */
  if (argc > 1 && JS_IsFunction (argv[1]) && !JS_IsException (result)) {
    /* Create wrapper object to pass to reviver */
    JSValue wrapper = JS_NewObject (ctx);
    JS_SetPropertyStr (ctx, wrapper, "", result);

    JSValue holder = wrapper;
    JSValue key = JS_KEY_empty;
    JSValue args[2] = { key, JS_GetProperty (ctx, holder, key) };
    JSValue final = JS_Call (ctx, argv[1], holder, 2, args);
    result = final;
  }

  return result;
}

static const JSCFunctionListEntry js_cell_json_funcs[] = {
  JS_CFUNC_DEF ("encode", 4, js_cell_json_encode),
  JS_CFUNC_DEF ("decode", 2, js_cell_json_decode),
};

JSValue js_core_json_use (JSContext *ctx) {
  JSGCRef obj_ref;
  JS_PushGCRef (ctx, &obj_ref);
  obj_ref.val = JS_NewObject (ctx);
  JS_SetPropertyFunctionList (ctx, obj_ref.val, js_cell_json_funcs, countof (js_cell_json_funcs));
  JSValue result = obj_ref.val;
  JS_PopGCRef (ctx, &obj_ref);
  return result;
}
