/*
 * el_seed.c — El language seed runtime: minimal C OS boundary
 *
 * This file exposes all OS-boundary primitives that El programs need, under
 * the __ prefix convention. It is self-contained: all allocators, arena
 * management, and el_request_start / el_request_end are defined here.
 *
 * Threading: __thread_create / __thread_join use dlsym(RTLD_DEFAULT) to look
 * up El function symbols at runtime. This is the foundation of El's parallelism.
 *
 * Link: cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
 *           -o <out> <prog>.c el_seed.c
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "el_seed.h"

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <pthread.h>
#include <dlfcn.h>
#include <curl/curl.h>

/* ── Private allocator ───────────────────────────────────────────────────── */
/*
 * el_seed.c carries its own arena for per-request allocation tracking.
 * The arena is reset at el_request_start / el_request_end, which are defined
 * below and delegate to seed_request_start / seed_request_end.
 */

#define SEED_ARENA_INITIAL 512

typedef struct {
    char**  ptrs;
    size_t  count;
    size_t  cap;
} SeedArena;

static _Thread_local SeedArena _seed_arena    = {NULL, 0, 0};
static _Thread_local int       _seed_arena_on = 0;

static void seed_arena_track(char* p) {
    if (!_seed_arena_on || !p) return;
    if (_seed_arena.count >= _seed_arena.cap) {
        size_t nc = _seed_arena.cap == 0 ? SEED_ARENA_INITIAL : _seed_arena.cap * 2;
        char** g = realloc(_seed_arena.ptrs, nc * sizeof(char*));
        if (!g) return;
        _seed_arena.ptrs = g;
        _seed_arena.cap  = nc;
    }
    _seed_arena.ptrs[_seed_arena.count++] = p;
}

static void seed_request_start(void) {
    _seed_arena.count = 0;
    _seed_arena_on    = 1;
}

static void seed_request_end(void) {
    _seed_arena_on = 0;
    for (size_t i = 0; i < _seed_arena.count; i++) free(_seed_arena.ptrs[i]);
    _seed_arena.count = 0;
}

/* el_request_start / el_request_end — formerly defined in el_runtime.c.
 * Now self-contained in el_seed.c, delegating to the seed arena. */
void el_request_start(void) { seed_request_start(); }
void el_request_end(void)   { seed_request_end(); }

/* Persistent alloc — bypasses arena (state, engram internals). */
static char* seed_strdup_persist(const char* s) {
    if (!s) return strdup("");
    return strdup(s);
}

static char* seed_strdup(const char* s) {
    char* p = strdup(s ? s : "");
    seed_arena_track(p);
    return p;
}

static char* seed_strbuf(size_t n) {
    char* p = malloc(n + 1);
    if (!p) { fputs("el_seed: out of memory\n", stderr); exit(1); }
    p[0] = '\0';
    seed_arena_track(p);
    return p;
}

static el_val_t seed_wrap_str(char* s) { return EL_STR(s); }

/* ── String primitives ───────────────────────────────────────────────────── */

el_val_t __str_len(el_val_t s) {
    const char* p = EL_CSTR(s);
    if (!p) return 0;
    return (el_val_t)strlen(p);
}

el_val_t __str_char_at(el_val_t s, el_val_t i) {
    const char* p = EL_CSTR(s);
    if (!p) return 0;
    int64_t len = (int64_t)strlen(p);
    int64_t idx = (int64_t)i;
    if (idx < 0 || idx >= len) return 0;
    return (el_val_t)(unsigned char)p[idx];
}

el_val_t __str_alloc(el_val_t n) {
    int64_t sz = (int64_t)n;
    if (sz < 0) sz = 0;
    char* buf = seed_strbuf((size_t)sz);
    memset(buf, 0, (size_t)sz + 1);
    return seed_wrap_str(buf);
}

el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c) {
    char* p = (char*)(uintptr_t)s;
    if (!p) return s;
    int64_t len = (int64_t)strlen(p);
    int64_t idx = (int64_t)i;
    if (idx < 0 || idx >= len) return s;
    p[idx] = (char)(unsigned char)(int64_t)c;
    return s;
}

el_val_t __str_cmp(el_val_t a, el_val_t b) {
    const char* sa = EL_CSTR(a);
    const char* sb = EL_CSTR(b);
    if (!sa) sa = "";
    if (!sb) sb = "";
    return (el_val_t)strcmp(sa, sb);
}

el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n) {
    const char* sa = EL_CSTR(a);
    const char* sb = EL_CSTR(b);
    if (!sa) sa = "";
    if (!sb) sb = "";
    return (el_val_t)strncmp(sa, sb, (size_t)(int64_t)n);
}

el_val_t __str_concat_raw(el_val_t a, el_val_t b) {
    const char* sa = EL_CSTR(a);
    const char* sb = EL_CSTR(b);
    if (!sa) sa = "";
    if (!sb) sb = "";
    size_t la = strlen(sa), lb = strlen(sb);
    char* out = seed_strbuf(la + lb);
    memcpy(out, sa, la);
    memcpy(out + la, sb, lb);
    out[la + lb] = '\0';
    return seed_wrap_str(out);
}

el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end) {
    const char* p = EL_CSTR(s);
    if (!p) return seed_wrap_str(seed_strdup(""));
    int64_t len = (int64_t)strlen(p);
    int64_t st  = (int64_t)start;
    int64_t en  = (int64_t)end;
    if (st < 0) st = 0;
    if (en > len) en = len;
    if (st >= en) return seed_wrap_str(seed_strdup(""));
    int64_t sz = en - st;
    char* out = seed_strbuf((size_t)sz);
    memcpy(out, p + st, (size_t)sz);
    out[sz] = '\0';
    return seed_wrap_str(out);
}

el_val_t __int_to_str(el_val_t n) {
    char buf[32];
    snprintf(buf, sizeof(buf), "%lld", (long long)(int64_t)n);
    return seed_wrap_str(seed_strdup(buf));
}

el_val_t __str_to_int(el_val_t s) {
    const char* p = EL_CSTR(s);
    if (!p) return 0;
    return (el_val_t)atoll(p);
}

el_val_t __float_to_str(el_val_t f) {
    char buf[64];
    snprintf(buf, sizeof(buf), "%g", el_to_float(f));
    return seed_wrap_str(seed_strdup(buf));
}

el_val_t __str_to_float(el_val_t s) {
    const char* p = EL_CSTR(s);
    if (!p) return el_from_float(0.0);
    return el_from_float(strtod(p, NULL));
}

/* ── I/O ─────────────────────────────────────────────────────────────────── */

void __println(el_val_t s) {
    const char* p = EL_CSTR(s);
    puts(p ? p : "");
}

void __print(el_val_t s) {
    const char* p = EL_CSTR(s);
    if (p) fputs(p, stdout);
}

el_val_t __readline(void) {
    char buf[4096];
    if (!fgets(buf, sizeof(buf), stdin)) return seed_wrap_str(seed_strdup(""));
    size_t len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0';
    return seed_wrap_str(seed_strdup(buf));
}

/* ── Filesystem ──────────────────────────────────────────────────────────── */

el_val_t __fs_read(el_val_t path) {
    const char* p = EL_CSTR(path);
    if (!p) return seed_wrap_str(seed_strdup(""));
    FILE* f = fopen(p, "rb");
    if (!f) return seed_wrap_str(seed_strdup(""));
    fseek(f, 0, SEEK_END);
    long sz = ftell(f);
    rewind(f);
    if (sz < 0) { fclose(f); return seed_wrap_str(seed_strdup("")); }
    char* buf = seed_strbuf((size_t)sz);
    size_t got = fread(buf, 1, (size_t)sz, f);
    buf[got] = '\0';
    fclose(f);
    return seed_wrap_str(buf);
}

el_val_t __fs_write(el_val_t path, el_val_t content) {
    const char* p = EL_CSTR(path);
    const char* c = EL_CSTR(content);
    if (!p || !c) return 0;
    FILE* f = fopen(p, "wb");
    if (!f) return 0;
    size_t n = strlen(c);
    size_t w = fwrite(c, 1, n, f);
    fclose(f);
    return w == n ? 1 : 0;
}

el_val_t __fs_exists(el_val_t path) {
    const char* p = EL_CSTR(path);
    if (!p || !*p) return 0;
    struct stat st;
    return (el_val_t)(stat(p, &st) == 0 ? 1 : 0);
}

el_val_t __fs_list_raw(el_val_t path) {
    const char* p = EL_CSTR(path);
    if (!p) return seed_wrap_str(seed_strdup(""));
    DIR* d = opendir(p);
    if (!d) return seed_wrap_str(seed_strdup(""));
    /* Build newline-separated list of filenames. */
    size_t cap = 4096, len = 0;
    char* buf = malloc(cap);
    if (!buf) { closedir(d); return seed_wrap_str(seed_strdup("")); }
    buf[0] = '\0';
    struct dirent* e;
    while ((e = readdir(d)) != NULL) {
        if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue;
        size_t nlen = strlen(e->d_name);
        while (len + nlen + 2 >= cap) {
            cap *= 2;
            char* g = realloc(buf, cap);
            if (!g) { free(buf); closedir(d); return seed_wrap_str(seed_strdup("")); }
            buf = g;
        }
        if (len > 0) buf[len++] = '\n';
        memcpy(buf + len, e->d_name, nlen);
        len += nlen;
        buf[len] = '\0';
    }
    closedir(d);
    seed_arena_track(buf);
    return seed_wrap_str(buf);
}

el_val_t __fs_mkdir(el_val_t path) {
    const char* p = EL_CSTR(path);
    if (!p || !*p) return 0;
    size_t n = strlen(p);
    char* buf = malloc(n + 1);
    if (!buf) return 0;
    memcpy(buf, p, n + 1);
    for (size_t i = 1; i <= n; i++) {
        if (buf[i] == '/' || buf[i] == '\0') {
            char saved = buf[i];
            buf[i] = '\0';
            if (buf[0] != '\0') {
                if (mkdir(buf, 0755) != 0 && errno != EEXIST) {
                    struct stat st;
                    if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) {
                        free(buf); return 0;
                    }
                }
            }
            buf[i] = saved;
        }
    }
    free(buf);
    return 1;
}

el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n) {
    const char* p = EL_CSTR(path);
    const char* b = EL_CSTR(bytes);
    int64_t     sz = (int64_t)n;
    if (!p || !b || sz < 0) return 0;
    FILE* f = fopen(p, "wb");
    if (!f) return 0;
    size_t written = (sz > 0) ? fwrite(b, 1, (size_t)sz, f) : 0;
    int ok1 = (fflush(f) == 0);
    int ok2 = (fclose(f) == 0);
    if (!ok1 || !ok2 || written != (size_t)sz) { remove(p); return 0; }
    return 1;
}

/* ── HTTP client ─────────────────────────────────────────────────────────── */

typedef struct {
    char*  data;
    size_t len;
    size_t cap;
} SeedHttpBuf;

static void seed_httpbuf_init(SeedHttpBuf* b) {
    b->cap = 1024; b->len = 0;
    b->data = malloc(b->cap);
    if (!b->data) { fputs("el_seed: out of memory\n", stderr); exit(1); }
    b->data[0] = '\0';
}

static void seed_httpbuf_append(SeedHttpBuf* b, const void* src, size_t n) {
    while (b->len + n + 1 > b->cap) b->cap *= 2;
    b->data = realloc(b->data, b->cap);
    if (!b->data) { fputs("el_seed: out of memory\n", stderr); exit(1); }
    memcpy(b->data + b->len, src, n);
    b->len += n;
    b->data[b->len] = '\0';
}

static size_t seed_http_write_cb(char* ptr, size_t size, size_t nmemb, void* ud) {
    size_t n = size * nmemb;
    seed_httpbuf_append((SeedHttpBuf*)ud, ptr, n);
    return n;
}

/* Build a curl_slist from a JSON object string of header name:value pairs. */
static struct curl_slist* seed_headers_from_json(const char* hj) {
    struct curl_slist* h = NULL;
    if (!hj || !*hj) return NULL;
    /* Walk key:value pairs at depth 1. Simple parser — same logic as json_find_key
     * in el_runtime.c but adapted for building curl headers. */
    const char* p = hj;
    while (*p && *p != '{') p++;
    if (*p == '{') p++;
    while (*p) {
        while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',') p++;
        if (*p == '}' || *p == '\0') break;
        if (*p != '"') break;
        /* Parse key */
        p++;
        const char* ks = p;
        while (*p && *p != '"') { if (*p == '\\') p++; p++; }
        size_t klen = (size_t)(p - ks);
        if (*p == '"') p++;
        /* Skip : */
        while (*p == ' ' || *p == ':') p++;
        /* Parse value */
        if (*p != '"') break;
        p++;
        const char* vs = p;
        while (*p && *p != '"') { if (*p == '\\') p++; p++; }
        size_t vlen = (size_t)(p - vs);
        if (*p == '"') p++;
        /* Build "Key: Value" header line */
        size_t line_len = klen + 2 + vlen + 1;
        char* line = malloc(line_len);
        if (line) {
            memcpy(line, ks, klen);
            memcpy(line + klen, ": ", 2);
            memcpy(line + klen + 2, vs, vlen);
            line[klen + 2 + vlen] = '\0';
            h = curl_slist_append(h, line);
            free(line);
        }
    }
    return h;
}

static el_val_t seed_http_error_json(const char* msg) {
    if (!msg) msg = "unknown error";
    size_t n = strlen(msg) * 6 + 20;
    char* buf = seed_strbuf(n);
    /* Simple escape: replace " with \" */
    char* d = buf;
    *d++ = '{'; *d++ = '"'; *d++ = 'e'; *d++ = 'r'; *d++ = 'r';
    *d++ = 'o'; *d++ = 'r'; *d++ = '"'; *d++ = ':'; *d++ = '"';
    for (const char* s = msg; *s; s++) {
        if (*s == '"' || *s == '\\') *d++ = '\\';
        *d++ = *s;
    }
    *d++ = '"'; *d++ = '}'; *d = '\0';
    return seed_wrap_str(buf);
}

el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body,
                   el_val_t headers_json, el_val_t timeout_ms) {
    const char* m  = EL_CSTR(method);
    const char* u  = EL_CSTR(url);
    const char* b  = EL_CSTR(body);
    const char* hj = EL_CSTR(headers_json);
    int64_t     tms = (int64_t)timeout_ms;
    if (tms <= 0) tms = 60000;

    if (!u || !*u) return seed_http_error_json("empty url");

    CURL* c = curl_easy_init();
    if (!c) return seed_http_error_json("curl_easy_init failed");

    SeedHttpBuf rb; seed_httpbuf_init(&rb);
    char errbuf[CURL_ERROR_SIZE]; errbuf[0] = '\0';

    curl_easy_setopt(c, CURLOPT_URL, u);
    curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, seed_http_write_cb);
    curl_easy_setopt(c, CURLOPT_WRITEDATA, &rb);
    curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, (long)tms);
    curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(c, CURLOPT_ERRORBUFFER, errbuf);
    curl_easy_setopt(c, CURLOPT_USERAGENT, "el-seed/1.0");

    struct curl_slist* hdrs = seed_headers_from_json(hj);
    if (hdrs) curl_easy_setopt(c, CURLOPT_HTTPHEADER, hdrs);

    if (m && strcmp(m, "POST") == 0) {
        curl_easy_setopt(c, CURLOPT_POST, 1L);
        curl_easy_setopt(c, CURLOPT_POSTFIELDS, b ? b : "");
        curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)(b ? strlen(b) : 0));
    } else if (m && strcmp(m, "PUT") == 0) {
        curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "PUT");
        if (b) {
            curl_easy_setopt(c, CURLOPT_POSTFIELDS, b);
            curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)strlen(b));
        }
    } else if (m && strcmp(m, "DELETE") == 0) {
        curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "DELETE");
    } else if (m && strcmp(m, "PATCH") == 0) {
        curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, "PATCH");
        if (b) {
            curl_easy_setopt(c, CURLOPT_POSTFIELDS, b);
            curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)strlen(b));
        }
    }
    /* GET is the default */

    CURLcode rc = curl_easy_perform(c);
    if (hdrs) curl_slist_free_all(hdrs);
    curl_easy_cleanup(c);

    if (rc != CURLE_OK) {
        free(rb.data);
        const char* em = errbuf[0] ? errbuf : curl_easy_strerror(rc);
        return seed_http_error_json(em);
    }

    seed_arena_track(rb.data);
    return seed_wrap_str(rb.data);
}

static size_t seed_http_file_write_cb(char* ptr, size_t size, size_t nmemb, void* ud) {
    return fwrite(ptr, size, nmemb, (FILE*)ud);
}

el_val_t __http_do_to_file(el_val_t method, el_val_t url, el_val_t body,
                            el_val_t headers_json, el_val_t out_path) {
    const char* m   = EL_CSTR(method);
    const char* u   = EL_CSTR(url);
    const char* b   = EL_CSTR(body);
    const char* hj  = EL_CSTR(headers_json);
    const char* op  = EL_CSTR(out_path);

    if (!u || !*u || !op || !*op) return 0;
    FILE* f = fopen(op, "wb");
    if (!f) return 0;

    CURL* c = curl_easy_init();
    if (!c) { fclose(f); remove(op); return 0; }

    char errbuf[CURL_ERROR_SIZE]; errbuf[0] = '\0';
    curl_easy_setopt(c, CURLOPT_URL, u);
    curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, seed_http_file_write_cb);
    curl_easy_setopt(c, CURLOPT_WRITEDATA, f);
    curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 60000L);
    curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(c, CURLOPT_ERRORBUFFER, errbuf);
    curl_easy_setopt(c, CURLOPT_USERAGENT, "el-seed/1.0");

    struct curl_slist* hdrs = seed_headers_from_json(hj);
    if (hdrs) curl_easy_setopt(c, CURLOPT_HTTPHEADER, hdrs);

    if (m && strcmp(m, "POST") == 0) {
        curl_easy_setopt(c, CURLOPT_POST, 1L);
        curl_easy_setopt(c, CURLOPT_POSTFIELDS, b ? b : "");
        curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)(b ? strlen(b) : 0));
    }

    CURLcode rc = curl_easy_perform(c);
    if (hdrs) curl_slist_free_all(hdrs);
    curl_easy_cleanup(c);

    int ok1 = (fflush(f) == 0);
    int ok2 = (fclose(f) == 0);

    if (rc != CURLE_OK || !ok1 || !ok2) { remove(op); return 0; }
    return 1;
}

/* ── HTTP server ─────────────────────────────────────────────────────────── */
/* Delegate to el_runtime.c's http_serve / http_serve_v2 via the existing
 * http_set_handler mechanism. */

/* Forward declarations for el_runtime.c functions called from el_seed.c.
 * A full #include "el_runtime.h" causes redefinition conflicts (el_seed.h
 * already defines el_to_float, el_from_float, and several __ wrappers).
 * Declare only the symbols actually used here. */

/* HTTP server */
el_val_t http_serve(el_val_t port, el_val_t handler);
el_val_t http_serve_v2(el_val_t port, el_val_t handler);
el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body);

/* JSON */
el_val_t json_get(el_val_t json, el_val_t key);
el_val_t json_get_raw(el_val_t json_str, el_val_t key);
el_val_t json_parse(el_val_t s);
el_val_t json_stringify(el_val_t v);
el_val_t json_array_len(el_val_t json_str);
el_val_t json_array_get(el_val_t json_str, el_val_t index);
el_val_t json_array_get_string(el_val_t json_str, el_val_t index);
el_val_t json_get_string(el_val_t json_str, el_val_t key);
el_val_t json_get_int(el_val_t json_str, el_val_t key);
el_val_t json_get_float(el_val_t json_str, el_val_t key);
el_val_t json_get_bool(el_val_t json_str, el_val_t key);
el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value);

/* URL / HTML */
el_val_t url_encode(el_val_t s);
el_val_t url_decode(el_val_t s);
el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json);

/* State K/V */
el_val_t state_set(el_val_t key, el_val_t value);
el_val_t state_get(el_val_t key);
el_val_t state_del(el_val_t key);
el_val_t state_keys(void);
el_val_t state_get_or(el_val_t key, el_val_t default_val);

/* Engram graph */
el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience);
el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
                          el_val_t salience, el_val_t importance, el_val_t confidence,
                          el_val_t tier, el_val_t tags_json);
el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
                              el_val_t salience, el_val_t importance, el_val_t confidence,
                              el_val_t tier, el_val_t tags_json, el_val_t layer_id);
el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible,
                          el_val_t transparent, el_val_t injectable);
el_val_t engram_remove_layer(el_val_t layer_id);
el_val_t engram_list_layers(void);
el_val_t engram_get_node(el_val_t id);
void     engram_strengthen(el_val_t node_id);
void     engram_forget(el_val_t node_id);
el_val_t engram_node_count(void);
el_val_t engram_search(el_val_t query, el_val_t limit);
el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset);
void     engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id);
el_val_t engram_neighbors(el_val_t node_id);
el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction);
el_val_t engram_edge_count(void);
el_val_t engram_activate(el_val_t query, el_val_t depth);
el_val_t engram_save(el_val_t path);
el_val_t engram_load(el_val_t path);
el_val_t engram_get_node_json(el_val_t id);
el_val_t engram_search_json(el_val_t query, el_val_t limit);
el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset);
el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset);
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
el_val_t engram_activate_json(el_val_t query, el_val_t depth);
el_val_t engram_stats_json(void);
el_val_t engram_list_layers_json(void);
el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth);

void __http_serve(el_val_t port, el_val_t handler_name) {
    http_serve(port, handler_name);
}

void __http_serve_v2(el_val_t port, el_val_t handler_name) {
    http_serve_v2(port, handler_name);
}

el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body) {
    return http_response(status, headers_json, body);
}

/* ── HTTP SSE — Server-Sent Events streaming ─────────────────────────────── */
/*
 * Thread-local file descriptor stashed by http_worker_v2 before calling the
 * El handler. El SSE builtins read this to get the raw socket fd.
 *
 * Lifecycle:
 *   http_worker_v2 sets _tl_http_conn_fd = fd  (via el_seed_set_http_conn_fd)
 *   El handler calls __http_conn_fd()  → receives that fd
 *   El handler calls __http_sse_open(fd) → sends SSE headers, keeps fd open
 *   El handler calls __http_sse_send(fd, data) → writes "data: ...\n\n"
 *   El handler calls __http_sse_close(fd) → closes the fd
 *   El handler returns "__sse__" sentinel → http_worker_v2 does NOT close fd
 *
 * The -1 value means no current connection (guard against misuse outside
 * a handler context).
 */
static __thread int _tl_http_conn_fd = -1;

/* Called by el_runtime.c's http_worker_v2 — not part of the El ABI. */
void el_seed_set_http_conn_fd(int fd) {
    _tl_http_conn_fd = fd;
}

/* __http_conn_fd() — returns the raw fd for the current HTTP connection.
 * Valid only inside an http_serve_v2 handler before it returns. */
el_val_t __http_conn_fd(void) {
    return EL_INT(_tl_http_conn_fd);
}

/* __http_sse_open(fd) — sends SSE response headers on fd, keeping it open.
 * Returns 1 on success, 0 on write failure. */
el_val_t __http_sse_open(el_val_t conn_id) {
    int fd = (int)(int64_t)conn_id;
    if (fd < 0) return 0;
    static const char sse_headers[] =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/event-stream\r\n"
        "Cache-Control: no-cache\r\n"
        "Connection: keep-alive\r\n"
        "Access-Control-Allow-Origin: *\r\n"
        "\r\n";
    size_t n = sizeof(sse_headers) - 1;  /* exclude NUL */
    size_t sent = 0;
    while (sent < n) {
        ssize_t w = write(fd, sse_headers + sent, n - sent);
        if (w <= 0) return 0;
        sent += (size_t)w;
    }
    return 1;
}

/* __http_sse_send(fd, data) — writes one SSE event frame: "data: <data>\n\n".
 * data must not contain newlines. Returns 1 on success, 0 on client disconnect. */
el_val_t __http_sse_send(el_val_t conn_id, el_val_t data) {
    int fd = (int)(int64_t)conn_id;
    if (fd < 0) return 0;
    const char* s = EL_CSTR(data);
    if (!s) s = "";
    /* Build "data: <s>\n\n" in a single buffer for one write call. */
    size_t prefix_len = 6;  /* "data: " */
    size_t slen = strlen(s);
    size_t total = prefix_len + slen + 2;  /* + "\n\n" */
    char* buf = malloc(total + 1);
    if (!buf) return 0;
    memcpy(buf, "data: ", 6);
    memcpy(buf + 6, s, slen);
    buf[6 + slen]     = '\n';
    buf[6 + slen + 1] = '\n';
    buf[total] = '\0';
    size_t sent = 0;
    int ok = 1;
    while (sent < total) {
        ssize_t w = write(fd, buf + sent, total - sent);
        if (w <= 0) { ok = 0; break; }
        sent += (size_t)w;
    }
    free(buf);
    return ok ? 1 : 0;
}

/* __http_sse_close(fd) — closes the SSE connection fd. */
el_val_t __http_sse_close(el_val_t conn_id) {
    int fd = (int)(int64_t)conn_id;
    if (fd < 0) return 0;
    close(fd);
    return 1;
}

/* ── Threading ───────────────────────────────────────────────────────────── */
/*
 * Design:
 *   Static ElThread table (max EL_SEED_MAX_THREADS entries).
 *   __thread_create: pick a free slot, store fn_name + arg, launch pthread.
 *   Worker: dlsym(RTLD_DEFAULT, fn_name) → call as el_val_t fn(el_val_t).
 *           Store result string in slot.result.
 *   __thread_join: pthread_join, return stored result string.
 *
 * Thread handle is the slot index (0..EL_SEED_MAX_THREADS-1).
 * Returns -1 on failure (no slots, dlsym failure, pthread_create failure).
 *
 * Each slot is guarded by its own mutex so join/create on different handles
 * never contend.
 */

#define EL_SEED_MAX_THREADS 64

typedef el_val_t (*ElFn1)(el_val_t);

typedef struct {
    int         in_use;
    char*       fn_name;
    char*       arg;
    char*       result;
    pthread_t   tid;
    int         done;   /* set to 1 by worker before exit */
} ElThread;

static ElThread          _el_threads[EL_SEED_MAX_THREADS];
static pthread_mutex_t   _el_thread_mu = PTHREAD_MUTEX_INITIALIZER;

typedef struct {
    int slot;
} ElThreadArg;

static void* el_thread_worker(void* raw) {
    ElThreadArg* ta = (ElThreadArg*)raw;
    int slot = ta->slot;
    free(ta);

    ElThread* t = &_el_threads[slot];

    /* Resolve the El function symbol in the running binary. */
    void* sym = dlsym(RTLD_DEFAULT, t->fn_name);
    if (!sym) {
        pthread_mutex_lock(&_el_thread_mu);
        t->result = seed_strdup_persist("");
        t->done   = 1;
        pthread_mutex_unlock(&_el_thread_mu);
        return NULL;
    }

    ElFn1 fn = (ElFn1)sym;

    /* Call the El function with the string argument. */
    el_val_t arg_val = EL_STR(t->arg);
    el_val_t ret     = fn(arg_val);

    /* Persist the result string. */
    const char* rs = EL_CSTR(ret);
    char* stored   = seed_strdup_persist(rs ? rs : "");

    pthread_mutex_lock(&_el_thread_mu);
    t->result = stored;
    t->done   = 1;
    pthread_mutex_unlock(&_el_thread_mu);

    return NULL;
}

el_val_t __thread_create(el_val_t fn_name, el_val_t arg) {
    const char* fname = EL_CSTR(fn_name);
    const char* astr  = EL_CSTR(arg);
    if (!fname || !*fname) return (el_val_t)(int64_t)-1;
    if (!astr) astr = "";

    pthread_mutex_lock(&_el_thread_mu);
    int slot = -1;
    for (int i = 0; i < EL_SEED_MAX_THREADS; i++) {
        if (!_el_threads[i].in_use) { slot = i; break; }
    }
    if (slot < 0) {
        pthread_mutex_unlock(&_el_thread_mu);
        return (el_val_t)(int64_t)-1;
    }

    ElThread* t = &_el_threads[slot];
    t->in_use  = 1;
    t->done    = 0;
    t->fn_name = seed_strdup_persist(fname);
    t->arg     = seed_strdup_persist(astr);
    t->result  = NULL;
    pthread_mutex_unlock(&_el_thread_mu);

    ElThreadArg* ta = malloc(sizeof(ElThreadArg));
    if (!ta) {
        pthread_mutex_lock(&_el_thread_mu);
        free(t->fn_name); free(t->arg);
        t->in_use = 0;
        pthread_mutex_unlock(&_el_thread_mu);
        return (el_val_t)(int64_t)-1;
    }
    ta->slot = slot;

    if (pthread_create(&t->tid, NULL, el_thread_worker, ta) != 0) {
        free(ta);
        pthread_mutex_lock(&_el_thread_mu);
        free(t->fn_name); free(t->arg);
        t->in_use = 0;
        pthread_mutex_unlock(&_el_thread_mu);
        return (el_val_t)(int64_t)-1;
    }

    return (el_val_t)(int64_t)slot;
}

el_val_t __thread_join(el_val_t tid) {
    int64_t slot = (int64_t)tid;
    if (slot < 0 || slot >= EL_SEED_MAX_THREADS) return seed_wrap_str(seed_strdup(""));

    ElThread* t = &_el_threads[slot];

    pthread_mutex_lock(&_el_thread_mu);
    if (!t->in_use) {
        pthread_mutex_unlock(&_el_thread_mu);
        return seed_wrap_str(seed_strdup(""));
    }
    pthread_mutex_unlock(&_el_thread_mu);

    /* Wait for thread to finish. */
    pthread_join(t->tid, NULL);

    pthread_mutex_lock(&_el_thread_mu);
    char* res = t->result ? t->result : "";
    char* copy = seed_strdup(res);   /* arena-tracked for caller lifetime */
    free(t->fn_name);
    free(t->arg);
    if (t->result) { free(t->result); t->result = NULL; }
    t->fn_name = NULL; t->arg = NULL;
    t->in_use  = 0;
    t->done    = 0;
    pthread_mutex_unlock(&_el_thread_mu);

    return seed_wrap_str(copy);
}

/* ── Mutex pool ──────────────────────────────────────────────────────────── */

#define EL_SEED_MAX_MUTEXES 256

static pthread_mutex_t  _el_mutexes[EL_SEED_MAX_MUTEXES];
static int              _el_mutex_init[EL_SEED_MAX_MUTEXES];
static int              _el_mutex_used[EL_SEED_MAX_MUTEXES];
static pthread_mutex_t  _el_mutex_pool_mu = PTHREAD_MUTEX_INITIALIZER;

el_val_t __mutex_new(void) {
    pthread_mutex_lock(&_el_mutex_pool_mu);
    int slot = -1;
    for (int i = 0; i < EL_SEED_MAX_MUTEXES; i++) {
        if (!_el_mutex_used[i]) { slot = i; break; }
    }
    if (slot < 0) {
        pthread_mutex_unlock(&_el_mutex_pool_mu);
        return (el_val_t)(int64_t)-1;
    }
    _el_mutex_used[slot] = 1;
    if (!_el_mutex_init[slot]) {
        pthread_mutex_init(&_el_mutexes[slot], NULL);
        _el_mutex_init[slot] = 1;
    }
    pthread_mutex_unlock(&_el_mutex_pool_mu);
    return (el_val_t)(int64_t)slot;
}

void __mutex_lock(el_val_t m) {
    int64_t slot = (int64_t)m;
    if (slot < 0 || slot >= EL_SEED_MAX_MUTEXES) return;
    if (!_el_mutex_init[slot]) return;
    pthread_mutex_lock(&_el_mutexes[slot]);
}

void __mutex_unlock(el_val_t m) {
    int64_t slot = (int64_t)m;
    if (slot < 0 || slot >= EL_SEED_MAX_MUTEXES) return;
    if (!_el_mutex_init[slot]) return;
    pthread_mutex_unlock(&_el_mutexes[slot]);
}

/* ── Subprocess ──────────────────────────────────────────────────────────── */

el_val_t __exec(el_val_t cmd) {
    const char* c = EL_CSTR(cmd);
    if (!c) return seed_wrap_str(seed_strdup(""));
    FILE* f = popen(c, "r");
    if (!f) return seed_wrap_str(seed_strdup(""));
    size_t cap = 4096, len = 0;
    char* buf = malloc(cap);
    if (!buf) { pclose(f); return seed_wrap_str(seed_strdup("")); }
    char tmp[4096];
    while (fgets(tmp, sizeof(tmp), f)) {
        size_t n = strlen(tmp);
        while (len + n + 1 > cap) {
            cap *= 2;
            char* g = realloc(buf, cap);
            if (!g) { free(buf); pclose(f); return seed_wrap_str(seed_strdup("")); }
            buf = g;
        }
        memcpy(buf + len, tmp, n);
        len += n;
        buf[len] = '\0';
    }
    pclose(f);
    seed_arena_track(buf);
    return seed_wrap_str(buf);
}

void __exec_bg(el_val_t cmd) {
    const char* c = EL_CSTR(cmd);
    if (!c) return;
    /* Fork-free: run in background via system() with & appended. */
    size_t n = strlen(c);
    char* s = malloc(n + 4);
    if (!s) return;
    memcpy(s, c, n);
    memcpy(s + n, " &", 3);
    system(s);
    free(s);
}

/* ── Environment and process ─────────────────────────────────────────────── */

el_val_t __env_get(el_val_t key) {
    const char* k = EL_CSTR(key);
    if (!k) return seed_wrap_str(seed_strdup(""));
    const char* v = getenv(k);
    return seed_wrap_str(seed_strdup(v ? v : ""));
}

void __exit_program(el_val_t code) {
    exit((int)(int64_t)code);
}

/* ── args_json ────────────────────────────────────────────────────────────── */

static int    _seed_argc = 0;
static char** _seed_argv = NULL;

void el_seed_init_args(int argc, char** argv) {
    _seed_argc = argc;
    _seed_argv = argv;
}

el_val_t __args_json(void) {
    /* Return ["arg1","arg2",...] as a JSON string. Skip argv[0] (program name). */
    size_t cap = 256, len = 0;
    char* buf = malloc(cap);
    if (!buf) return seed_wrap_str(seed_strdup("[]"));
    buf[len++] = '[';
    int first = 1;
    for (int i = 1; i < _seed_argc; i++) {
        const char* a = _seed_argv[i];
        /* Estimate: each arg needs at most strlen*6+4 bytes (worst case escape) */
        size_t need = strlen(a) * 6 + 8;
        while (len + need + 2 > cap) {
            cap *= 2;
            char* g = realloc(buf, cap);
            if (!g) { free(buf); return seed_wrap_str(seed_strdup("[]")); }
            buf = g;
        }
        if (!first) buf[len++] = ',';
        first = 0;
        buf[len++] = '"';
        for (const char* p = a; *p; p++) {
            unsigned char c = (unsigned char)*p;
            if (c == '"')       { buf[len++] = '\\'; buf[len++] = '"'; }
            else if (c == '\\') { buf[len++] = '\\'; buf[len++] = '\\'; }
            else if (c == '\n') { buf[len++] = '\\'; buf[len++] = 'n'; }
            else if (c == '\r') { buf[len++] = '\\'; buf[len++] = 'r'; }
            else if (c == '\t') { buf[len++] = '\\'; buf[len++] = 't'; }
            else buf[len++] = (char)c;
        }
        buf[len++] = '"';
    }
    buf[len++] = ']';
    buf[len]   = '\0';
    seed_arena_track(buf);
    return seed_wrap_str(buf);
}

/* ── Time ────────────────────────────────────────────────────────────────── */

el_val_t __time_now_ns(void) {
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
        int64_t ns = (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
        return (el_val_t)ns;
    }
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (el_val_t)((int64_t)tv.tv_sec * 1000000000LL + (int64_t)tv.tv_usec * 1000LL);
}

void __sleep_ms(el_val_t ms) {
    int64_t m = (int64_t)ms;
    if (m < 0) m = 0;
    struct timespec ts;
    ts.tv_sec  = (time_t)(m / 1000LL);
    ts.tv_nsec = (long)((m % 1000LL) * 1000000LL);
    nanosleep(&ts, NULL);
}

/* ── UUID ────────────────────────────────────────────────────────────────── */

static int _seed_uuid_seeded = 0;

el_val_t __uuid_v4(void) {
    if (!_seed_uuid_seeded) {
        srand((unsigned)time(NULL) ^ (unsigned)(uintptr_t)&_seed_uuid_seeded);
        _seed_uuid_seeded = 1;
    }
    unsigned char b[16];
    for (int i = 0; i < 16; i++) b[i] = (unsigned char)(rand() & 0xff);
    b[6] = (b[6] & 0x0f) | 0x40;  /* version 4 */
    b[8] = (b[8] & 0x3f) | 0x80;  /* RFC 4122 variant */
    char buf[37];
    snprintf(buf, sizeof(buf),
             "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             b[0],b[1],b[2],b[3], b[4],b[5], b[6],b[7],
             b[8],b[9], b[10],b[11],b[12],b[13],b[14],b[15]);
    return seed_wrap_str(seed_strdup(buf));
}

/* ── Math ────────────────────────────────────────────────────────────────── */

el_val_t __sqrt_f(el_val_t f) { return el_from_float(sqrt(el_to_float(f))); }
el_val_t __log_f(el_val_t f)  { return el_from_float(log10(el_to_float(f))); }
el_val_t __ln_f(el_val_t f)   { return el_from_float(log(el_to_float(f))); }
el_val_t __sin_f(el_val_t f)  { return el_from_float(sin(el_to_float(f))); }
el_val_t __cos_f(el_val_t f)  { return el_from_float(cos(el_to_float(f))); }
el_val_t __pi_f(void)         { return el_from_float(3.14159265358979323846); }

/* ── JSON — thin wrappers around el_runtime.c implementations ────────────── */

el_val_t __json_get(el_val_t json, el_val_t key)                      { return json_get(json, key); }
el_val_t __json_get_raw(el_val_t json_str, el_val_t key)              { return json_get_raw(json_str, key); }
el_val_t __json_parse(el_val_t s)                                      { return json_parse(s); }
el_val_t __json_stringify(el_val_t v)                                  { return json_stringify(v); }
el_val_t __json_parse_map(el_val_t json_str)                           { return json_parse(json_str); }
el_val_t __json_stringify_val(el_val_t val)                            { return json_stringify(val); }
el_val_t __json_array_len(el_val_t json_str)                           { return json_array_len(json_str); }
el_val_t __json_array_get(el_val_t json_str, el_val_t index)          { return json_array_get(json_str, index); }
el_val_t __json_array_get_string(el_val_t json_str, el_val_t index)   { return json_array_get_string(json_str, index); }
el_val_t __json_get_string(el_val_t json_str, el_val_t key)           { return json_get_string(json_str, key); }
el_val_t __json_get_int(el_val_t json_str, el_val_t key)              { return json_get_int(json_str, key); }
el_val_t __json_get_float(el_val_t json_str, el_val_t key)            { return json_get_float(json_str, key); }
el_val_t __json_get_bool(el_val_t json_str, el_val_t key)             { return json_get_bool(json_str, key); }
el_val_t __json_set(el_val_t json_str, el_val_t key, el_val_t value)  { return json_set(json_str, key, value); }

/* ── State K/V — thin wrappers ───────────────────────────────────────────── */

el_val_t __state_set(el_val_t key, el_val_t value) { return state_set(key, value); }
el_val_t __state_get(el_val_t key)                  { return state_get(key); }
el_val_t __state_del(el_val_t key)                  { return state_del(key); }
el_val_t __state_keys(void)                          { return state_keys(); }

/* ── HTML/URL — thin wrappers ────────────────────────────────────────────── */

el_val_t __html_sanitize(el_val_t input_html, el_val_t allowlist_json) {
    return el_html_sanitize(input_html, allowlist_json);
}

el_val_t __url_encode(el_val_t s) { return url_encode(s); }
el_val_t __url_decode(el_val_t s) { return url_decode(s); }

/* ── Engram — thin wrappers ──────────────────────────────────────────────── */

el_val_t __engram_node(el_val_t content, el_val_t node_type, el_val_t salience) {
    return engram_node(content, node_type, salience);
}

el_val_t __engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
                             el_val_t salience, el_val_t importance, el_val_t confidence,
                             el_val_t tier, el_val_t tags) {
    return engram_node_full(content, node_type, label, salience, importance, confidence, tier, tags);
}

el_val_t __engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
                                el_val_t salience, el_val_t certainty, el_val_t confidence,
                                el_val_t status, el_val_t tags, el_val_t layer_id) {
    return engram_node_layered(content, node_type, label, salience, certainty, confidence,
                               status, tags, layer_id);
}

el_val_t __engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible,
                             el_val_t transparent, el_val_t injectable) {
    return engram_add_layer(name, priority, suppressible, transparent, injectable);
}

el_val_t __engram_remove_layer(el_val_t layer_id)       { return engram_remove_layer(layer_id); }
el_val_t __engram_list_layers(void)                      { return engram_list_layers(); }
el_val_t __engram_get_node(el_val_t id)                  { return engram_get_node(id); }
void     __engram_strengthen(el_val_t node_id)           { engram_strengthen(node_id); }
void     __engram_forget(el_val_t node_id)               { engram_forget(node_id); }
el_val_t __engram_node_count(void)                       { return engram_node_count(); }

el_val_t __engram_search(el_val_t query, el_val_t limit)  { return engram_search(query, limit); }
el_val_t __engram_scan_nodes(el_val_t limit, el_val_t offset) { return engram_scan_nodes(limit, offset); }

void __engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation) {
    engram_connect(from_id, to_id, weight, relation);
}

el_val_t __engram_edge_between(el_val_t from_id, el_val_t to_id) {
    return engram_edge_between(from_id, to_id);
}

el_val_t __engram_neighbors(el_val_t node_id) { return engram_neighbors(node_id); }

el_val_t __engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction) {
    return engram_neighbors_filtered(node_id, max_depth, direction);
}

el_val_t __engram_edge_count(void) { return engram_edge_count(); }

el_val_t __engram_activate(el_val_t query, el_val_t depth) { return engram_activate(query, depth); }
el_val_t __engram_save(el_val_t path)                       { return engram_save(path); }
el_val_t __engram_load(el_val_t path)                       { return engram_load(path); }

el_val_t __engram_get_node_json(el_val_t id)               { return engram_get_node_json(id); }

el_val_t __engram_search_json(el_val_t query, el_val_t limit) {
    return engram_search_json(query, limit);
}

el_val_t __engram_scan_nodes_json(el_val_t limit, el_val_t offset) {
    return engram_scan_nodes_json(limit, offset);
}

el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset) {
    return engram_scan_nodes_by_type_json(node_type, limit, offset);
}

el_val_t __engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction) {
    return engram_neighbors_json(node_id, max_depth, direction);
}

el_val_t __engram_activate_json(el_val_t query, el_val_t depth) {
    return engram_activate_json(query, depth);
}

el_val_t __engram_stats_json(void)                         { return engram_stats_json(); }
el_val_t __engram_list_layers_json(void)                   { return engram_list_layers_json(); }

el_val_t __engram_compile_layered_json(el_val_t intent, el_val_t depth) {
    return engram_compile_layered_json(intent, depth);
}

/* ── Cryptographic hashing ────────────────────────────────────────────────── */
/*
 * SHA-256 — self-contained implementation (no OpenSSL dependency).
 * Based on Brad Conte's public-domain reference implementation.
 */

typedef struct {
    uint8_t  data[64];
    uint32_t datalen;
    uint64_t bitlen;
    uint32_t state[8];
} _seed_sha256_ctx;

static const uint32_t _seed_sha256_k[64] = {
    0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
    0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
    0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
    0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
    0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
    0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
    0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
    0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};

#define _SEED_ROTR32(x,n) (((x)>>(n))|((x)<<(32-(n))))
#define _SEED_CH(x,y,z)  (((x)&(y))^(~(x)&(z)))
#define _SEED_MAJ(x,y,z) (((x)&(y))^((x)&(z))^((y)&(z)))
#define _SEED_EP0(x)     (_SEED_ROTR32(x,2)^_SEED_ROTR32(x,13)^_SEED_ROTR32(x,22))
#define _SEED_EP1(x)     (_SEED_ROTR32(x,6)^_SEED_ROTR32(x,11)^_SEED_ROTR32(x,25))
#define _SEED_SIG0(x)    (_SEED_ROTR32(x,7)^_SEED_ROTR32(x,18)^((x)>>3))
#define _SEED_SIG1(x)    (_SEED_ROTR32(x,17)^_SEED_ROTR32(x,19)^((x)>>10))

static void _seed_sha256_transform(_seed_sha256_ctx* ctx, const uint8_t* data) {
    uint32_t a,b,c,d,e,f,g,h,t1,t2,m[64];
    for (int i=0,j=0; i<16; i++,j+=4)
        m[i]=(uint32_t)(data[j]<<24)|(data[j+1]<<16)|(data[j+2]<<8)|data[j+3];
    for (int i=16; i<64; i++)
        m[i]=_SEED_SIG1(m[i-2])+m[i-7]+_SEED_SIG0(m[i-15])+m[i-16];
    a=ctx->state[0]; b=ctx->state[1]; c=ctx->state[2]; d=ctx->state[3];
    e=ctx->state[4]; f=ctx->state[5]; g=ctx->state[6]; h=ctx->state[7];
    for (int i=0; i<64; i++) {
        t1=h+_SEED_EP1(e)+_SEED_CH(e,f,g)+_seed_sha256_k[i]+m[i];
        t2=_SEED_EP0(a)+_SEED_MAJ(a,b,c);
        h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
    }
    ctx->state[0]+=a; ctx->state[1]+=b; ctx->state[2]+=c; ctx->state[3]+=d;
    ctx->state[4]+=e; ctx->state[5]+=f; ctx->state[6]+=g; ctx->state[7]+=h;
}

static void _seed_sha256_init(_seed_sha256_ctx* ctx) {
    ctx->datalen=0; ctx->bitlen=0;
    ctx->state[0]=0x6a09e667; ctx->state[1]=0xbb67ae85;
    ctx->state[2]=0x3c6ef372; ctx->state[3]=0xa54ff53a;
    ctx->state[4]=0x510e527f; ctx->state[5]=0x9b05688c;
    ctx->state[6]=0x1f83d9ab; ctx->state[7]=0x5be0cd19;
}

static void _seed_sha256_update(_seed_sha256_ctx* ctx, const uint8_t* data, size_t len) {
    for (size_t i=0; i<len; i++) {
        ctx->data[ctx->datalen++] = data[i];
        if (ctx->datalen==64) { _seed_sha256_transform(ctx,ctx->data); ctx->bitlen+=512; ctx->datalen=0; }
    }
}

static void _seed_sha256_final(_seed_sha256_ctx* ctx, uint8_t hash[32]) {
    uint32_t i=ctx->datalen;
    ctx->data[i++]=0x80;
    if (ctx->datalen<56) { while(i<56) ctx->data[i++]=0; }
    else { while(i<64) ctx->data[i++]=0; _seed_sha256_transform(ctx,ctx->data); memset(ctx->data,0,56); }
    ctx->bitlen+=ctx->datalen*8;
    ctx->data[63]=(uint8_t)(ctx->bitlen); ctx->data[62]=(uint8_t)(ctx->bitlen>>8);
    ctx->data[61]=(uint8_t)(ctx->bitlen>>16); ctx->data[60]=(uint8_t)(ctx->bitlen>>24);
    ctx->data[59]=(uint8_t)(ctx->bitlen>>32); ctx->data[58]=(uint8_t)(ctx->bitlen>>40);
    ctx->data[57]=(uint8_t)(ctx->bitlen>>48); ctx->data[56]=(uint8_t)(ctx->bitlen>>56);
    _seed_sha256_transform(ctx,ctx->data);
    for (i=0; i<4; i++) {
        hash[i]    =(uint8_t)(ctx->state[0]>>(24-i*8));
        hash[i+4]  =(uint8_t)(ctx->state[1]>>(24-i*8));
        hash[i+8]  =(uint8_t)(ctx->state[2]>>(24-i*8));
        hash[i+12] =(uint8_t)(ctx->state[3]>>(24-i*8));
        hash[i+16] =(uint8_t)(ctx->state[4]>>(24-i*8));
        hash[i+20] =(uint8_t)(ctx->state[5]>>(24-i*8));
        hash[i+24] =(uint8_t)(ctx->state[6]>>(24-i*8));
        hash[i+28] =(uint8_t)(ctx->state[7]>>(24-i*8));
    }
}

/* ── Manifest reader (shared: macOS + Linux) ─────────────────────────────── */
/*
 * Parse the app{} block from a manifest.el file.
 * Expected format (subset — only app{} block is parsed):
 *
 *   app {
 *       window_title "My App"
 *       window_width 1200
 *       window_height 800
 *       min_width 600
 *       min_height 400
 *   }
 *
 * Returns JSON: {"title":"...","width":N,"height":N,"min_width":N,"min_height":N}
 * Returns "{}" on parse failure.
 *
 * Compiled unconditionally so both EL_TARGET_MACOS and EL_TARGET_LINUX can use
 * it — the __manifest_read builtin is identical on all native targets.
 */

static void seed_manifest_skip_ws(const char** p) {
    while (**p && (unsigned char)**p <= ' ') (*p)++;
}

/* Read a quoted string literal. Caller must free() the result. */
static char* seed_manifest_read_string(const char** p) {
    seed_manifest_skip_ws(p);
    if (**p != '"') return NULL;
    (*p)++;
    const char* start = *p;
    while (**p && **p != '"') {
        if (**p == '\\') (*p)++; /* skip escaped char */
        (*p)++;
    }
    size_t len = (size_t)(*p - start);
    if (**p == '"') (*p)++;
    char* out = malloc(len + 1);
    if (!out) return NULL;
    memcpy(out, start, len);
    out[len] = '\0';
    return out;
}

/* Read an unquoted integer literal. */
static int64_t seed_manifest_read_int(const char** p) {
    seed_manifest_skip_ws(p);
    int64_t result = 0;
    int neg = 0;
    if (**p == '-') { neg = 1; (*p)++; }
    while (**p >= '0' && **p <= '9') {
        result = result * 10 + (**p - '0');
        (*p)++;
    }
    return neg ? -result : result;
}

/* Find "app {" block start. Returns pointer past the '{'. */
static const char* seed_manifest_find_app_block(const char* src) {
    while (*src) {
        /* Skip line comments (// ...) */
        if (src[0] == '/' && src[1] == '/') {
            while (*src && *src != '\n') src++;
            continue;
        }
        if (strncmp(src, "app", 3) == 0 &&
            (src[3] == ' ' || src[3] == '\t' || src[3] == '\n' || src[3] == '{')) {
            src += 3;
            while (*src && *src != '{') src++;
            if (*src == '{') return src + 1;
            return NULL;
        }
        src++;
    }
    return NULL;
}

el_val_t __manifest_read(el_val_t path) {
    const char* p = EL_CSTR(path);
    if (!p || !*p) return seed_wrap_str(seed_strdup("{}"));

    /* Read the file. */
    FILE* f = fopen(p, "r");
    if (!f) return seed_wrap_str(seed_strdup("{}"));
    fseek(f, 0, SEEK_END);
    long sz = ftell(f);
    rewind(f);
    if (sz <= 0) { fclose(f); return seed_wrap_str(seed_strdup("{}")); }
    char* src = malloc((size_t)sz + 1);
    if (!src) { fclose(f); return seed_wrap_str(seed_strdup("{}")); }
    size_t got = fread(src, 1, (size_t)sz, f);
    src[got] = '\0';
    fclose(f);

    const char* app_start = seed_manifest_find_app_block(src);
    if (!app_start) { free(src); return seed_wrap_str(seed_strdup("{}")); }

    /* Parse key-value pairs inside the app { } block. */
    char* title      = NULL;
    int64_t width    = 1200;
    int64_t height   = 800;
    int64_t min_w    = 600;
    int64_t min_h    = 400;

    const char* cur = app_start;
    while (*cur && *cur != '}') {
        /* Skip whitespace and comments. */
        while (*cur && (unsigned char)*cur <= ' ') cur++;
        if (*cur == '}') break;
        if (cur[0] == '/' && cur[1] == '/') {
            while (*cur && *cur != '\n') cur++;
            continue;
        }

        /* Read key token. */
        const char* key_start = cur;
        while (*cur && (unsigned char)*cur > ' ' && *cur != '{' && *cur != '}') cur++;
        size_t key_len = (size_t)(cur - key_start);
        if (key_len == 0) { cur++; continue; }

        /* Skip whitespace between key and value. */
        while (*cur == ' ' || *cur == '\t') cur++;

        if (strncmp(key_start, "window_title", key_len) == 0 && key_len == 12) {
            free(title);
            title = seed_manifest_read_string(&cur);
        } else if (strncmp(key_start, "window_width", key_len) == 0 && key_len == 12) {
            width = seed_manifest_read_int(&cur);
        } else if (strncmp(key_start, "window_height", key_len) == 0 && key_len == 13) {
            height = seed_manifest_read_int(&cur);
        } else if (strncmp(key_start, "min_width", key_len) == 0 && key_len == 9) {
            min_w = seed_manifest_read_int(&cur);
        } else if (strncmp(key_start, "min_height", key_len) == 0 && key_len == 10) {
            min_h = seed_manifest_read_int(&cur);
        } else {
            /* Unknown key — skip to end of line. */
            while (*cur && *cur != '\n' && *cur != '}') cur++;
        }
    }

    free(src);

    /* Build JSON result. */
    const char* t = title ? title : "App";
    size_t buf_sz = strlen(t) * 6 + 128;
    char* buf = seed_strbuf(buf_sz);
    snprintf(buf, buf_sz,
        "{\"title\":\"%s\",\"width\":%lld,\"height\":%lld,\"min_width\":%lld,\"min_height\":%lld}",
        t, (long long)width, (long long)height, (long long)min_w, (long long)min_h);
    free(title);
    return seed_wrap_str(buf);
}

/* ── Native widget system — macOS AppKit ─────────────────────────────────── */
/*
 * These builtins are compiled in only when EL_TARGET_MACOS is defined.
 * They call through to el_appkit.m (Objective-C) which implements the actual
 * AppKit widget creation and management.
 *
 * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the
 * el_appkit_* function, return the result.
 *
 * Compile requirements:
 *   - Compile el_appkit.m alongside this file (clang -ObjC -framework Cocoa)
 *   - Pass -DEL_TARGET_MACOS to enable this section
 */

#ifdef EL_TARGET_MACOS

/* Forward declarations — implemented in el_appkit.m */
extern void    el_appkit_init(void);
extern void    el_appkit_run_loop(void);
extern int64_t el_appkit_window_create(const char* title, int w, int h, int mw, int mh);
extern void    el_appkit_window_show(int64_t handle);
extern void    el_appkit_window_set_title(int64_t handle, const char* title);
extern int64_t el_appkit_vstack_create(int spacing);
extern int64_t el_appkit_hstack_create(int spacing);
extern int64_t el_appkit_zstack_create(void);
extern int64_t el_appkit_scroll_create(void);
extern int64_t el_appkit_label_create(const char* text);
extern int64_t el_appkit_button_create(const char* label);
extern int64_t el_appkit_text_field_create(const char* placeholder);
extern int64_t el_appkit_text_area_create(const char* placeholder);
extern int64_t el_appkit_image_create(const char* path_or_name);
extern void    el_appkit_widget_set_text(int64_t handle, const char* text);
extern const char* el_appkit_widget_get_text(int64_t handle);
extern void    el_appkit_widget_set_color(int64_t h, float r, float g, float b, float a);
extern void    el_appkit_widget_set_bg_color(int64_t h, float r, float g, float b, float a);
extern void    el_appkit_widget_set_font(int64_t h, const char* family, int size, int bold);
extern void    el_appkit_widget_set_padding(int64_t h, int top, int right, int bottom, int left);
extern void    el_appkit_widget_set_width(int64_t h, int width);
extern void    el_appkit_widget_set_height(int64_t h, int height);
extern void    el_appkit_widget_set_flex(int64_t h, int flex);
extern void    el_appkit_widget_set_corner_radius(int64_t h, int radius);
extern void    el_appkit_widget_set_disabled(int64_t h, int disabled);
extern void    el_appkit_widget_set_hidden(int64_t h, int hidden);
extern void    el_appkit_widget_add_child(int64_t parent, int64_t child);
extern void    el_appkit_widget_remove_child(int64_t parent, int64_t child);
extern void    el_appkit_widget_destroy(int64_t handle);
extern void    el_appkit_widget_on_click(int64_t h, const char* fn_name);
extern void    el_appkit_widget_on_change(int64_t h, const char* fn_name);
extern void    el_appkit_widget_on_submit(int64_t h, const char* fn_name);

/* ── Native widget builtins ──────────────────────────────────────────────── */

void __native_init(void)      { el_appkit_init(); }
void __native_run_loop(void)  { el_appkit_run_loop(); }

el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
                          el_val_t min_width, el_val_t min_height) {
    return (el_val_t)el_appkit_window_create(
        EL_CSTR(title),
        (int)(int64_t)width, (int)(int64_t)height,
        (int)(int64_t)min_width, (int)(int64_t)min_height);
}

void __window_show(el_val_t h)                    { el_appkit_window_show((int64_t)h); }
void __window_set_title(el_val_t h, el_val_t t)   { el_appkit_window_set_title((int64_t)h, EL_CSTR(t)); }

el_val_t __vstack_create(el_val_t spacing)  { return (el_val_t)el_appkit_vstack_create((int)(int64_t)spacing); }
el_val_t __hstack_create(el_val_t spacing)  { return (el_val_t)el_appkit_hstack_create((int)(int64_t)spacing); }
el_val_t __zstack_create(void)              { return (el_val_t)el_appkit_zstack_create(); }
el_val_t __scroll_create(void)              { return (el_val_t)el_appkit_scroll_create(); }

el_val_t __label_create(el_val_t text)              { return (el_val_t)el_appkit_label_create(EL_CSTR(text)); }
el_val_t __button_create(el_val_t label)            { return (el_val_t)el_appkit_button_create(EL_CSTR(label)); }
el_val_t __text_field_create(el_val_t placeholder)  { return (el_val_t)el_appkit_text_field_create(EL_CSTR(placeholder)); }
el_val_t __text_area_create(el_val_t placeholder)   { return (el_val_t)el_appkit_text_area_create(EL_CSTR(placeholder)); }
el_val_t __image_create(el_val_t path_or_name)      { return (el_val_t)el_appkit_image_create(EL_CSTR(path_or_name)); }

void __widget_set_text(el_val_t h, el_val_t t)   { el_appkit_widget_set_text((int64_t)h, EL_CSTR(t)); }

el_val_t __widget_get_text(el_val_t h) {
    const char* s = el_appkit_widget_get_text((int64_t)h);
    if (!s) return seed_wrap_str(seed_strdup(""));
    /* el_appkit returns strdup'd string — wrap it (arena won't track it, but
     * caller must treat return as short-lived or copy it). */
    return EL_STR(s);
}

void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_appkit_widget_set_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_appkit_widget_set_bg_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) {
    el_appkit_widget_set_font((int64_t)h, EL_CSTR(family),
        (int)(int64_t)size, (int)(int64_t)bold);
}

void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right,
                           el_val_t bottom, el_val_t left) {
    el_appkit_widget_set_padding((int64_t)h,
        (int)(int64_t)top, (int)(int64_t)right,
        (int)(int64_t)bottom, (int)(int64_t)left);
}

void __widget_set_width(el_val_t h, el_val_t w)         { el_appkit_widget_set_width((int64_t)h, (int)(int64_t)w); }
void __widget_set_height(el_val_t h, el_val_t ht)       { el_appkit_widget_set_height((int64_t)h, (int)(int64_t)ht); }
void __widget_set_flex(el_val_t h, el_val_t f)          { el_appkit_widget_set_flex((int64_t)h, (int)(int64_t)f); }
void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_appkit_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); }
void __widget_set_disabled(el_val_t h, el_val_t d)      { el_appkit_widget_set_disabled((int64_t)h, (int)(int64_t)d); }
void __widget_set_hidden(el_val_t h, el_val_t hid)      { el_appkit_widget_set_hidden((int64_t)h, (int)(int64_t)hid); }
void __widget_add_child(el_val_t p, el_val_t c)         { el_appkit_widget_add_child((int64_t)p, (int64_t)c); }
void __widget_remove_child(el_val_t p, el_val_t c)      { el_appkit_widget_remove_child((int64_t)p, (int64_t)c); }
void __widget_destroy(el_val_t h)                        { el_appkit_widget_destroy((int64_t)h); }

void __widget_on_click(el_val_t h, el_val_t fn_name)    { el_appkit_widget_on_click((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_change(el_val_t h, el_val_t fn_name)   { el_appkit_widget_on_change((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_submit(el_val_t h, el_val_t fn_name)   { el_appkit_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); }

#endif /* EL_TARGET_MACOS */

/* ── Native widget system — Linux GTK4 ──────────────────────────────────── */
/*
 * These builtins are compiled in only when EL_TARGET_LINUX is defined.
 * They call through to el_gtk4.c which implements the GTK4 widget layer.
 *
 * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the
 * el_gtk4_* function, return the result.
 *
 * Compile requirements:
 *   - Compile el_gtk4.c alongside this file
 *       gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) el_gtk4.c -c ...
 *   - Pass -DEL_TARGET_LINUX to enable this section
 *   - Link with $(pkg-config --libs gtk4) -ldl
 *
 * __manifest_read is shared with the macOS path (implemented above in the
 * seed_manifest_* helpers) and compiled for both targets unconditionally.
 */

#ifdef EL_TARGET_LINUX

/* Forward declarations — implemented in el_gtk4.c */
extern void        el_gtk4_init(void);
extern void        el_gtk4_run_loop(void);
extern int64_t     el_gtk4_window_create(const char* title, int w, int h,
                                          int mw, int mh);
extern void        el_gtk4_window_show(int64_t handle);
extern void        el_gtk4_window_set_title(int64_t handle, const char* title);
extern int64_t     el_gtk4_vstack_create(int spacing);
extern int64_t     el_gtk4_hstack_create(int spacing);
extern int64_t     el_gtk4_zstack_create(void);
extern int64_t     el_gtk4_scroll_create(void);
extern int64_t     el_gtk4_label_create(const char* text);
extern int64_t     el_gtk4_button_create(const char* label);
extern int64_t     el_gtk4_text_field_create(const char* placeholder);
extern int64_t     el_gtk4_text_area_create(const char* placeholder);
extern int64_t     el_gtk4_image_create(const char* path_or_name);
extern void        el_gtk4_widget_set_text(int64_t handle, const char* text);
extern const char* el_gtk4_widget_get_text(int64_t handle);
extern void        el_gtk4_widget_set_color(int64_t h, float r, float g, float b, float a);
extern void        el_gtk4_widget_set_bg_color(int64_t h, float r, float g, float b, float a);
extern void        el_gtk4_widget_set_font(int64_t h, const char* family, int size, int bold);
extern void        el_gtk4_widget_set_padding(int64_t h, int top, int right, int bottom, int left);
extern void        el_gtk4_widget_set_width(int64_t h, int width);
extern void        el_gtk4_widget_set_height(int64_t h, int height);
extern void        el_gtk4_widget_set_flex(int64_t h, int flex);
extern void        el_gtk4_widget_set_corner_radius(int64_t h, int radius);
extern void        el_gtk4_widget_set_disabled(int64_t h, int disabled);
extern void        el_gtk4_widget_set_hidden(int64_t h, int hidden);
extern void        el_gtk4_widget_add_child(int64_t parent, int64_t child);
extern void        el_gtk4_widget_remove_child(int64_t parent, int64_t child);
extern void        el_gtk4_widget_destroy(int64_t handle);
extern void        el_gtk4_widget_on_click(int64_t h, const char* fn_name);
extern void        el_gtk4_widget_on_change(int64_t h, const char* fn_name);
extern void        el_gtk4_widget_on_submit(int64_t h, const char* fn_name);

void __native_init(void)      { el_gtk4_init(); }
void __native_run_loop(void)  { el_gtk4_run_loop(); }

el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
                          el_val_t min_width, el_val_t min_height) {
    return (el_val_t)el_gtk4_window_create(
        EL_CSTR(title),
        (int)(int64_t)width, (int)(int64_t)height,
        (int)(int64_t)min_width, (int)(int64_t)min_height);
}

void __window_show(el_val_t h)                   { el_gtk4_window_show((int64_t)h); }
void __window_set_title(el_val_t h, el_val_t t)  { el_gtk4_window_set_title((int64_t)h, EL_CSTR(t)); }

el_val_t __vstack_create(el_val_t spacing)  { return (el_val_t)el_gtk4_vstack_create((int)(int64_t)spacing); }
el_val_t __hstack_create(el_val_t spacing)  { return (el_val_t)el_gtk4_hstack_create((int)(int64_t)spacing); }
el_val_t __zstack_create(void)              { return (el_val_t)el_gtk4_zstack_create(); }
el_val_t __scroll_create(void)              { return (el_val_t)el_gtk4_scroll_create(); }

el_val_t __label_create(el_val_t text)             { return (el_val_t)el_gtk4_label_create(EL_CSTR(text)); }
el_val_t __button_create(el_val_t label)           { return (el_val_t)el_gtk4_button_create(EL_CSTR(label)); }
el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_gtk4_text_field_create(EL_CSTR(placeholder)); }
el_val_t __text_area_create(el_val_t placeholder)  { return (el_val_t)el_gtk4_text_area_create(EL_CSTR(placeholder)); }
el_val_t __image_create(el_val_t path_or_name)     { return (el_val_t)el_gtk4_image_create(EL_CSTR(path_or_name)); }

void __widget_set_text(el_val_t h, el_val_t t)    { el_gtk4_widget_set_text((int64_t)h, EL_CSTR(t)); }

el_val_t __widget_get_text(el_val_t h) {
    const char* s = el_gtk4_widget_get_text((int64_t)h);
    if (!s) return EL_STR("");
    return EL_STR(s); /* el_gtk4 returns strdup'd string */
}

void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_gtk4_widget_set_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_gtk4_widget_set_bg_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) {
    el_gtk4_widget_set_font((int64_t)h, EL_CSTR(family),
        (int)(int64_t)size, (int)(int64_t)bold);
}

void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right,
                           el_val_t bottom, el_val_t left) {
    el_gtk4_widget_set_padding((int64_t)h,
        (int)(int64_t)top, (int)(int64_t)right,
        (int)(int64_t)bottom, (int)(int64_t)left);
}

void __widget_set_width(el_val_t h, el_val_t w)         { el_gtk4_widget_set_width((int64_t)h, (int)(int64_t)w); }
void __widget_set_height(el_val_t h, el_val_t ht)       { el_gtk4_widget_set_height((int64_t)h, (int)(int64_t)ht); }
void __widget_set_flex(el_val_t h, el_val_t f)          { el_gtk4_widget_set_flex((int64_t)h, (int)(int64_t)f); }
void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_gtk4_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); }
void __widget_set_disabled(el_val_t h, el_val_t d)      { el_gtk4_widget_set_disabled((int64_t)h, (int)(int64_t)d); }
void __widget_set_hidden(el_val_t h, el_val_t hid)      { el_gtk4_widget_set_hidden((int64_t)h, (int)(int64_t)hid); }
void __widget_add_child(el_val_t p, el_val_t c)         { el_gtk4_widget_add_child((int64_t)p, (int64_t)c); }
void __widget_remove_child(el_val_t p, el_val_t c)      { el_gtk4_widget_remove_child((int64_t)p, (int64_t)c); }
void __widget_destroy(el_val_t h)                        { el_gtk4_widget_destroy((int64_t)h); }

void __widget_on_click(el_val_t h, el_val_t fn_name)    { el_gtk4_widget_on_click((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_change(el_val_t h, el_val_t fn_name)   { el_gtk4_widget_on_change((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_submit(el_val_t h, el_val_t fn_name)   { el_gtk4_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); }

#endif /* EL_TARGET_LINUX */

/* ── Native widget system — SDL2 (embedded / Pi) ─────────────────────────── */
/*
 * These builtins are compiled in only when EL_TARGET_SDL2 is defined.
 * They call through to el_sdl2.c which implements a retained-mode pixel
 * widget layer on top of SDL2 + SDL_ttf + SDL_image.
 *
 * Each builtin is a thin wrapper: marshal el_val_t ↔ C types, call the
 * el_sdl2_* function, return the result.
 *
 * Compile requirements:
 *   gcc -DEL_TARGET_SDL2 $(sdl2-config --cflags) el_sdl2.c -c ...
 *   Pass -DEL_TARGET_SDL2 to enable this section.
 * Link:
 *   $(sdl2-config --libs) -lSDL2_ttf -lSDL2_image -ldl
 *
 * __manifest_read is shared with the macOS/Linux paths (implemented above).
 */

#ifdef EL_TARGET_SDL2

/* Forward declarations — implemented in el_sdl2.c */
extern void        el_sdl2_init(void);
extern void        el_sdl2_run_loop(void);
extern int64_t     el_sdl2_window_create(const char* title, int w, int h,
                                          int mw, int mh);
extern void        el_sdl2_window_show(int64_t handle);
extern void        el_sdl2_window_set_title(int64_t handle, const char* title);
extern int64_t     el_sdl2_vstack_create(int spacing);
extern int64_t     el_sdl2_hstack_create(int spacing);
extern int64_t     el_sdl2_zstack_create(void);
extern int64_t     el_sdl2_scroll_create(void);
extern int64_t     el_sdl2_label_create(const char* text);
extern int64_t     el_sdl2_button_create(const char* label);
extern int64_t     el_sdl2_text_field_create(const char* placeholder);
extern int64_t     el_sdl2_text_area_create(const char* placeholder);
extern int64_t     el_sdl2_image_create(const char* path_or_name);
extern void        el_sdl2_widget_set_text(int64_t handle, const char* text);
extern const char* el_sdl2_widget_get_text(int64_t handle);
extern void        el_sdl2_widget_set_color(int64_t h, float r, float g, float b, float a);
extern void        el_sdl2_widget_set_bg_color(int64_t h, float r, float g, float b, float a);
extern void        el_sdl2_widget_set_font(int64_t h, const char* family, int size, int bold);
extern void        el_sdl2_widget_set_padding(int64_t h, int top, int right, int bottom, int left);
extern void        el_sdl2_widget_set_width(int64_t h, int width);
extern void        el_sdl2_widget_set_height(int64_t h, int height);
extern void        el_sdl2_widget_set_flex(int64_t h, int flex);
extern void        el_sdl2_widget_set_corner_radius(int64_t h, int radius);
extern void        el_sdl2_widget_set_disabled(int64_t h, int disabled);
extern void        el_sdl2_widget_set_hidden(int64_t h, int hidden);
extern void        el_sdl2_widget_add_child(int64_t parent, int64_t child);
extern void        el_sdl2_widget_remove_child(int64_t parent, int64_t child);
extern void        el_sdl2_widget_destroy(int64_t handle);
extern void        el_sdl2_widget_on_click(int64_t h, const char* fn_name);
extern void        el_sdl2_widget_on_change(int64_t h, const char* fn_name);
extern void        el_sdl2_widget_on_submit(int64_t h, const char* fn_name);

void __native_init(void)      { el_sdl2_init(); }
void __native_run_loop(void)  { el_sdl2_run_loop(); }

el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
                          el_val_t min_width, el_val_t min_height) {
    return (el_val_t)el_sdl2_window_create(
        EL_CSTR(title),
        (int)(int64_t)width, (int)(int64_t)height,
        (int)(int64_t)min_width, (int)(int64_t)min_height);
}

void __window_show(el_val_t h)                   { el_sdl2_window_show((int64_t)h); }
void __window_set_title(el_val_t h, el_val_t t)  { el_sdl2_window_set_title((int64_t)h, EL_CSTR(t)); }

el_val_t __vstack_create(el_val_t spacing)  { return (el_val_t)el_sdl2_vstack_create((int)(int64_t)spacing); }
el_val_t __hstack_create(el_val_t spacing)  { return (el_val_t)el_sdl2_hstack_create((int)(int64_t)spacing); }
el_val_t __zstack_create(void)              { return (el_val_t)el_sdl2_zstack_create(); }
el_val_t __scroll_create(void)              { return (el_val_t)el_sdl2_scroll_create(); }

el_val_t __label_create(el_val_t text)             { return (el_val_t)el_sdl2_label_create(EL_CSTR(text)); }
el_val_t __button_create(el_val_t label)           { return (el_val_t)el_sdl2_button_create(EL_CSTR(label)); }
el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_sdl2_text_field_create(EL_CSTR(placeholder)); }
el_val_t __text_area_create(el_val_t placeholder)  { return (el_val_t)el_sdl2_text_area_create(EL_CSTR(placeholder)); }
el_val_t __image_create(el_val_t path_or_name)     { return (el_val_t)el_sdl2_image_create(EL_CSTR(path_or_name)); }

void __widget_set_text(el_val_t h, el_val_t t)    { el_sdl2_widget_set_text((int64_t)h, EL_CSTR(t)); }

el_val_t __widget_get_text(el_val_t h) {
    const char* s = el_sdl2_widget_get_text((int64_t)h);
    if (!s) return EL_STR("");
    return EL_STR(s);
}

void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_sdl2_widget_set_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_sdl2_widget_set_bg_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) {
    el_sdl2_widget_set_font((int64_t)h, EL_CSTR(family),
        (int)(int64_t)size, (int)(int64_t)bold);
}

void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right,
                           el_val_t bottom, el_val_t left) {
    el_sdl2_widget_set_padding((int64_t)h,
        (int)(int64_t)top, (int)(int64_t)right,
        (int)(int64_t)bottom, (int)(int64_t)left);
}

void __widget_set_width(el_val_t h, el_val_t w)         { el_sdl2_widget_set_width((int64_t)h, (int)(int64_t)w); }
void __widget_set_height(el_val_t h, el_val_t ht)       { el_sdl2_widget_set_height((int64_t)h, (int)(int64_t)ht); }
void __widget_set_flex(el_val_t h, el_val_t f)          { el_sdl2_widget_set_flex((int64_t)h, (int)(int64_t)f); }
void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_sdl2_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); }
void __widget_set_disabled(el_val_t h, el_val_t d)      { el_sdl2_widget_set_disabled((int64_t)h, (int)(int64_t)d); }
void __widget_set_hidden(el_val_t h, el_val_t hid)      { el_sdl2_widget_set_hidden((int64_t)h, (int)(int64_t)hid); }
void __widget_add_child(el_val_t p, el_val_t c)         { el_sdl2_widget_add_child((int64_t)p, (int64_t)c); }
void __widget_remove_child(el_val_t p, el_val_t c)      { el_sdl2_widget_remove_child((int64_t)p, (int64_t)c); }
void __widget_destroy(el_val_t h)                        { el_sdl2_widget_destroy((int64_t)h); }

void __widget_on_click(el_val_t h, el_val_t fn_name)    { el_sdl2_widget_on_click((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_change(el_val_t h, el_val_t fn_name)   { el_sdl2_widget_on_change((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_submit(el_val_t h, el_val_t fn_name)   { el_sdl2_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); }

#endif /* EL_TARGET_SDL2 */

/* ── EL_TARGET_LVGL — LVGL v9 embedded backend ───────────────────────────────
 *
 * These builtins are compiled in only when EL_TARGET_LVGL is defined.
 * Each wrapper translates el_val_t arguments (opaque int64_t) into the typed
 * parameters that el_lvgl_* expects, then calls through.
 *
 * Build:
 *   gcc -DEL_TARGET_LVGL -I./lvgl el_lvgl.c el_seed.c -c ...
 *   # Link with lvgl.a. For bare-metal add -DEL_LVGL_NO_DLSYM.
 *
 * __manifest_read is shared — defined unconditionally above.
 */

#ifdef EL_TARGET_LVGL

extern void    el_lvgl_init(void);
extern void    el_lvgl_run_loop(void);
extern int64_t el_lvgl_window_create(const char *title, int w, int h,
                                      int mw, int mh);
extern void    el_lvgl_window_show(int64_t handle);
extern void    el_lvgl_window_set_title(int64_t handle, const char *title);
extern int64_t el_lvgl_vstack_create(int spacing);
extern int64_t el_lvgl_hstack_create(int spacing);
extern int64_t el_lvgl_zstack_create(void);
extern int64_t el_lvgl_scroll_create(void);
extern int64_t el_lvgl_label_create(const char *text);
extern int64_t el_lvgl_button_create(const char *label);
extern int64_t el_lvgl_text_field_create(const char *placeholder);
extern int64_t el_lvgl_text_area_create(const char *placeholder);
extern int64_t el_lvgl_image_create(const char *path_or_name);
extern void    el_lvgl_widget_set_text(int64_t handle, const char *text);
extern const char *el_lvgl_widget_get_text(int64_t handle);
extern void    el_lvgl_widget_set_color(int64_t h, float r, float g,
                                         float b, float a);
extern void    el_lvgl_widget_set_bg_color(int64_t h, float r, float g,
                                            float b, float a);
extern void    el_lvgl_widget_set_font(int64_t h, const char *family,
                                        int size, int bold);
extern void    el_lvgl_widget_set_padding(int64_t h, int top, int right,
                                           int bottom, int left);
extern void    el_lvgl_widget_set_width(int64_t h, int width);
extern void    el_lvgl_widget_set_height(int64_t h, int height);
extern void    el_lvgl_widget_set_flex(int64_t h, int flex);
extern void    el_lvgl_widget_set_corner_radius(int64_t h, int radius);
extern void    el_lvgl_widget_set_disabled(int64_t h, int disabled);
extern void    el_lvgl_widget_set_hidden(int64_t h, int hidden);
extern void    el_lvgl_widget_add_child(int64_t parent, int64_t child);
extern void    el_lvgl_widget_remove_child(int64_t parent, int64_t child);
extern void    el_lvgl_widget_destroy(int64_t handle);
extern void    el_lvgl_widget_on_click(int64_t h, const char *fn_name);
extern void    el_lvgl_widget_on_change(int64_t h, const char *fn_name);
extern void    el_lvgl_widget_on_submit(int64_t h, const char *fn_name);

void __native_init(void)      { el_lvgl_init(); }
void __native_run_loop(void)  { el_lvgl_run_loop(); }

el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
                          el_val_t min_width, el_val_t min_height) {
    return (el_val_t)el_lvgl_window_create(
        EL_CSTR(title),
        (int)(int64_t)width,  (int)(int64_t)height,
        (int)(int64_t)min_width, (int)(int64_t)min_height);
}

void __window_show(el_val_t h)                   { el_lvgl_window_show((int64_t)h); }
void __window_set_title(el_val_t h, el_val_t t)  { el_lvgl_window_set_title((int64_t)h, EL_CSTR(t)); }

el_val_t __vstack_create(el_val_t spacing)  { return (el_val_t)el_lvgl_vstack_create((int)(int64_t)spacing); }
el_val_t __hstack_create(el_val_t spacing)  { return (el_val_t)el_lvgl_hstack_create((int)(int64_t)spacing); }
el_val_t __zstack_create(void)              { return (el_val_t)el_lvgl_zstack_create(); }
el_val_t __scroll_create(void)              { return (el_val_t)el_lvgl_scroll_create(); }

el_val_t __label_create(el_val_t text)             { return (el_val_t)el_lvgl_label_create(EL_CSTR(text)); }
el_val_t __button_create(el_val_t label)           { return (el_val_t)el_lvgl_button_create(EL_CSTR(label)); }
el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_lvgl_text_field_create(EL_CSTR(placeholder)); }
el_val_t __text_area_create(el_val_t placeholder)  { return (el_val_t)el_lvgl_text_area_create(EL_CSTR(placeholder)); }
el_val_t __image_create(el_val_t path_or_name)     { return (el_val_t)el_lvgl_image_create(EL_CSTR(path_or_name)); }

void __widget_set_text(el_val_t h, el_val_t t)    { el_lvgl_widget_set_text((int64_t)h, EL_CSTR(t)); }

el_val_t __widget_get_text(el_val_t h) {
    const char *s = el_lvgl_widget_get_text((int64_t)h);
    if (!s) return EL_STR("");
    return EL_STR(s); /* pointer into LVGL internal storage — valid until next LVGL op */
}

void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_lvgl_widget_set_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
    el_lvgl_widget_set_bg_color((int64_t)h,
        (float)el_to_float(r), (float)el_to_float(g),
        (float)el_to_float(b), (float)el_to_float(a));
}

void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) {
    el_lvgl_widget_set_font((int64_t)h, EL_CSTR(family),
        (int)(int64_t)size, (int)(int64_t)bold);
}

void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right,
                           el_val_t bottom, el_val_t left) {
    el_lvgl_widget_set_padding((int64_t)h,
        (int)(int64_t)top, (int)(int64_t)right,
        (int)(int64_t)bottom, (int)(int64_t)left);
}

void __widget_set_width(el_val_t h, el_val_t w)         { el_lvgl_widget_set_width((int64_t)h, (int)(int64_t)w); }
void __widget_set_height(el_val_t h, el_val_t ht)       { el_lvgl_widget_set_height((int64_t)h, (int)(int64_t)ht); }
void __widget_set_flex(el_val_t h, el_val_t f)          { el_lvgl_widget_set_flex((int64_t)h, (int)(int64_t)f); }
void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_lvgl_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); }
void __widget_set_disabled(el_val_t h, el_val_t d)      { el_lvgl_widget_set_disabled((int64_t)h, (int)(int64_t)d); }
void __widget_set_hidden(el_val_t h, el_val_t hid)      { el_lvgl_widget_set_hidden((int64_t)h, (int)(int64_t)hid); }
void __widget_add_child(el_val_t p, el_val_t c)         { el_lvgl_widget_add_child((int64_t)p, (int64_t)c); }
void __widget_remove_child(el_val_t p, el_val_t c)      { el_lvgl_widget_remove_child((int64_t)p, (int64_t)c); }
void __widget_destroy(el_val_t h)                        { el_lvgl_widget_destroy((int64_t)h); }

void __widget_on_click(el_val_t h, el_val_t fn_name)    { el_lvgl_widget_on_click((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_change(el_val_t h, el_val_t fn_name)   { el_lvgl_widget_on_change((int64_t)h, EL_CSTR(fn_name)); }
void __widget_on_submit(el_val_t h, el_val_t fn_name)   { el_lvgl_widget_on_submit((int64_t)h, EL_CSTR(fn_name)); }

#endif /* EL_TARGET_LVGL */

/* ── Cryptographic hashing ────────────────────────────────────────────────── */

el_val_t __sha256_hex(el_val_t sv) {
    const char* s = EL_CSTR(sv);
    if (!s) s = "";
    _seed_sha256_ctx ctx;
    _seed_sha256_init(&ctx);
    _seed_sha256_update(&ctx, (const uint8_t*)s, strlen(s));
    uint8_t digest[32];
    _seed_sha256_final(&ctx, digest);
    static const char hex[] = "0123456789abcdef";
    char* out = malloc(65);
    if (!out) return EL_STR("");
    for (int i=0; i<32; i++) {
        out[i*2]   = hex[(digest[i]>>4)&0xf];
        out[i*2+1] = hex[digest[i]&0xf];
    }
    out[64] = '\0';
    return EL_STR(out);
}
