/*
 * el_win32.c — Win32 backend for the el native widget system.
 *
 * This file implements the Windows Win32 widget layer that el_seed.c calls
 * through to when EL_TARGET_WIN32 is defined.
 *
 * Architecture:
 *   el program (el code)
 *       → __widget_* C builtins in el_seed.c
 *           → el_win32_* C functions declared here
 *               → HWND/Win32 API / GDI
 *
 * Widget handles: every widget (window, view, control) is assigned an int64_t
 * slot index into _el_widgets[]. The el program holds these as opaque Int values.
 * Slot 0 is never valid (reserved; handle -1 = invalid).
 *
 * Threading: Win32 UI must run on the main thread. el is single-threaded for
 * native UI, so all calls are direct. The message loop runs on the main thread
 * and never returns.
 *
 * Callback mechanism: when a widget fires an event (button click, text change),
 * the bridge looks up the registered callback function name, then calls
 *   GetProcAddress(GetModuleHandle(NULL), fn_name)(widget_handle, event_data)
 * This resolves the El function symbol at runtime, matching el_seed.c's
 * dlsym(RTLD_DEFAULT, fn_name) pattern on POSIX.
 *
 * Layout: VStack and HStack containers maintain a child HWND list and reflow
 * children on WM_SIZE using SetWindowPos. ZStack uses absolute positioning.
 *
 * Subclassing: SetWindowSubclass (comctl32) is used to intercept WM_COMMAND
 * and WM_NOTIFY on container windows so button clicks and edit changes route
 * back to el callbacks.
 *
 * String encoding: el strings are UTF-8 (const char*). All Win32 text APIs
 * use wide strings (WCHAR*). Conversion via MultiByteToWideChar /
 * WideCharToMultiByte throughout.
 *
 * Compile:
 *   cl /DEL_TARGET_WIN32 el_win32.c /c /Fo:el_win32.obj
 *   (link with: comctl32.lib user32.lib gdi32.lib)
 * or with clang-cl:
 *   clang-cl /DEL_TARGET_WIN32 -c el_win32.c -o el_win32.obj
 */

#ifdef EL_TARGET_WIN32

#define WIN32_LEAN_AND_MEAN
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif

#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "el_runtime.h"

/* ── Compile-time linkage ─────────────────────────────────────────────────── */
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

/* ── Custom window messages ───────────────────────────────────────────────── */
/* WM_USER+1: posted to a container to trigger a layout reflow pass. */
#define WM_EL_REFLOW  (WM_USER + 1)

/* ── Widget type enum ─────────────────────────────────────────────────────── */

#define EL_WIN32_MAX_WIDGETS 4096
#define EL_WIN32_MAX_CHILDREN 256

typedef enum {
    EL_WIDGET_FREE      = 0,
    EL_WIDGET_WINDOW    = 1,
    EL_WIDGET_VSTACK    = 2,
    EL_WIDGET_HSTACK    = 3,
    EL_WIDGET_ZSTACK    = 4,
    EL_WIDGET_SCROLL    = 5,
    EL_WIDGET_LABEL     = 6,
    EL_WIDGET_BUTTON    = 7,
    EL_WIDGET_TEXTFIELD = 8,
    EL_WIDGET_TEXTAREA  = 9,
    EL_WIDGET_IMAGE     = 10,
} ElWidgetKind;

/* Per-slot data. */
typedef struct {
    ElWidgetKind  kind;
    HWND          hwnd;        /* primary window handle; NULL if free */
    char*         cb_click;    /* El function name for click / submit events */
    char*         cb_change;   /* El function name for value-change events */

    /* Layout data (used by VSTACK / HSTACK / ZSTACK / WINDOW containers) */
    int           spacing;     /* pixels between children */
    int           pad_top;
    int           pad_right;
    int           pad_bottom;
    int           pad_left;
    int           flex;        /* flex weight; 0 = fixed size */

    /* Fixed size overrides (0 = not set) */
    int           fixed_w;
    int           fixed_h;

    /* Child list for layout containers */
    int64_t       children[EL_WIN32_MAX_CHILDREN];
    int           child_count;

    /* Color state (stored for WM_CTLCOLOR* replies) */
    COLORREF      fg_color;
    COLORREF      bg_color;
    HBRUSH        bg_brush;
    int           has_fg;
    int           has_bg;

    /* Font */
    HFONT         font;        /* NULL = default system font */

    /* Image: HBITMAP loaded for SS_BITMAP static */
    HBITMAP       hbmp;
} ElWidget;

static ElWidget _el_widgets[EL_WIN32_MAX_WIDGETS];
static int      _el_widget_count = 0;

/* ── Window class names ───────────────────────────────────────────────────── */
static const WCHAR* EL_WNDCLASS_VSTACK  = L"ElVStack";
static const WCHAR* EL_WNDCLASS_HSTACK  = L"ElHStack";
static const WCHAR* EL_WNDCLASS_ZSTACK  = L"ElZStack";
static const WCHAR* EL_WNDCLASS_SCROLL  = L"ElScroll";
static const WCHAR* EL_WNDCLASS_WINDOW  = L"ElWindow";

/* ── Forward declarations ─────────────────────────────────────────────────── */
static void el_win32_reflow(int64_t container_handle);
static LRESULT CALLBACK el_container_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK el_toplevel_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

/* ── String helpers ───────────────────────────────────────────────────────── */

/* UTF-8 → WCHAR*. Caller must free(). Returns NULL on failure. */
static WCHAR* el_utf8_to_wide(const char* s) {
    if (!s) s = "";
    int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
    if (n <= 0) return _wcsdup(L"");
    WCHAR* buf = (WCHAR*)malloc((size_t)n * sizeof(WCHAR));
    if (!buf) return _wcsdup(L"");
    MultiByteToWideChar(CP_UTF8, 0, s, -1, buf, n);
    return buf;
}

/* WCHAR* → UTF-8 char*. Caller must free(). Returns NULL on failure. */
static char* el_wide_to_utf8(const WCHAR* ws) {
    if (!ws) return strdup("");
    int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL);
    if (n <= 0) return strdup("");
    char* buf = (char*)malloc((size_t)n);
    if (!buf) return strdup("");
    WideCharToMultiByte(CP_UTF8, 0, ws, -1, buf, n, NULL, NULL);
    return buf;
}

/* ── Slot management ──────────────────────────────────────────────────────── */

static int64_t el_widget_alloc(ElWidgetKind kind, HWND hwnd) {
    for (int i = 1; i < EL_WIN32_MAX_WIDGETS; i++) {
        if (_el_widgets[i].kind == EL_WIDGET_FREE) {
            ElWidget* w = &_el_widgets[i];
            memset(w, 0, sizeof(ElWidget));
            w->kind  = kind;
            w->hwnd  = hwnd;
            if (i > _el_widget_count) _el_widget_count = i;
            return (int64_t)i;
        }
    }
    return -1;
}

static ElWidget* el_widget_get(int64_t handle) {
    if (handle <= 0 || handle >= EL_WIN32_MAX_WIDGETS) return NULL;
    if (_el_widgets[handle].kind == EL_WIDGET_FREE)    return NULL;
    return &_el_widgets[handle];
}

static void el_widget_free_slot(int64_t handle) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_click);  w->cb_click  = NULL;
    free(w->cb_change); w->cb_change = NULL;
    if (w->bg_brush) { DeleteObject(w->bg_brush); w->bg_brush = NULL; }
    if (w->font)     { DeleteObject(w->font);     w->font     = NULL; }
    if (w->hbmp)     { DeleteObject(w->hbmp);     w->hbmp     = NULL; }
    w->hwnd = NULL;
    w->kind = EL_WIDGET_FREE;
}

/* Map HWND → widget slot (O(n) linear scan; table is small). */
static int64_t el_hwnd_to_slot(HWND hwnd) {
    for (int i = 1; i <= _el_widget_count; i++) {
        if (_el_widgets[i].kind != EL_WIDGET_FREE && _el_widgets[i].hwnd == hwnd)
            return (int64_t)i;
    }
    return -1;
}

/* ── El callback invocation ──────────────────────────────────────────────── */
/*
 * Invoke an El callback by symbol name. El functions have the signature:
 *   fn handler(handle: Int, data: String) -> Void
 * compiled to:  void fn_name(el_val_t handle, el_val_t data)
 */
typedef void (*ElCb2)(int64_t handle, int64_t data);

static void el_win32_invoke_cb(const char* fn_name, int64_t handle, const char* data) {
    if (!fn_name || !*fn_name) return;
    FARPROC proc = GetProcAddress(GetModuleHandleW(NULL), fn_name);
    if (!proc) return;
    ElCb2 fn = (ElCb2)(void*)proc;
    fn(handle, (int64_t)(uintptr_t)(data ? data : ""));
}

/* ── Layout reflow ────────────────────────────────────────────────────────── */
/*
 * Reflow the direct children of a container widget.
 *
 * For VSTACK: stack children top-to-bottom, respecting padding and spacing.
 *   Children with flex > 0 divide the leftover height proportionally.
 *   Children with fixed_h > 0 use that height; otherwise QueryWidget.
 *
 * For HSTACK: same logic, left-to-right.
 *
 * For WINDOW: its content area is the client rect; children stacked vertically
 *   (WINDOW acts like an outer VSTACK for direct child attachment purposes).
 *
 * For ZSTACK: children fill the entire client rect (absolute; no reflow).
 *
 * For SCROLL: the single child fills the client rect width; height is its
 *   natural height (scroll bar handles overflow).
 */
static void el_win32_reflow(int64_t handle) {
    ElWidget* pw = el_widget_get(handle);
    if (!pw || !pw->hwnd) return;

    RECT cr;
    GetClientRect(pw->hwnd, &cr);
    int total_w = cr.right  - cr.left;
    int total_h = cr.bottom - cr.top;

    int pt = pw->pad_top;
    int pr = pw->pad_right;
    int pb = pw->pad_bottom;
    int pl = pw->pad_left;
    int sp = pw->spacing;

    int n = pw->child_count;
    if (n == 0) return;

    if (pw->kind == EL_WIDGET_ZSTACK) {
        /* All children fill the client rect. */
        int cw = total_w - pl - pr;
        int ch = total_h - pt - pb;
        if (cw < 0) cw = 0;
        if (ch < 0) ch = 0;
        for (int i = 0; i < n; i++) {
            ElWidget* cw_ = el_widget_get(pw->children[i]);
            if (!cw_ || !cw_->hwnd) continue;
            SetWindowPos(cw_->hwnd, NULL, pl, pt, cw, ch,
                         SWP_NOZORDER | SWP_NOACTIVATE);
        }
        return;
    }

    if (pw->kind == EL_WIDGET_SCROLL) {
        /* Single child fills width; natural height. */
        if (n < 1) return;
        ElWidget* cw_ = el_widget_get(pw->children[0]);
        if (!cw_ || !cw_->hwnd) return;
        int cw = total_w - pl - pr;
        if (cw < 0) cw = 0;
        /* Measure natural height. */
        RECT cr2;
        GetWindowRect(cw_->hwnd, &cr2);
        int ch = cr2.bottom - cr2.top;
        if (cw_->fixed_h > 0) ch = cw_->fixed_h;
        SetWindowPos(cw_->hwnd, NULL, pl, pt, cw, ch,
                     SWP_NOZORDER | SWP_NOACTIVATE);
        /* Update scroll range. */
        SCROLLINFO si;
        si.cbSize = sizeof(si);
        si.fMask  = SIF_RANGE | SIF_PAGE;
        si.nMin   = 0;
        si.nMax   = ch + pt + pb;
        si.nPage  = (UINT)total_h;
        SetScrollInfo(pw->hwnd, SB_VERT, &si, TRUE);
        return;
    }

    /* VSTACK, HSTACK, WINDOW */
    int is_vert = (pw->kind == EL_WIDGET_VSTACK  ||
                   pw->kind == EL_WIDGET_WINDOW);

    /* First pass: determine fixed sizes and total flex weight. */
    int  total_flex   = 0;
    int  fixed_used   = 0; /* pixels consumed by fixed children + spacing */

    /* Per-child resolved size in the main axis. */
    int* sizes = (int*)malloc((size_t)n * sizeof(int));
    if (!sizes) return;

    for (int i = 0; i < n; i++) {
        ElWidget* cw_ = el_widget_get(pw->children[i]);
        sizes[i] = 0;
        if (!cw_ || !cw_->hwnd) continue;
        int fixed = is_vert ? cw_->fixed_h : cw_->fixed_w;
        if (fixed > 0) {
            sizes[i] = fixed;
            fixed_used += fixed;
        } else if (cw_->flex > 0) {
            total_flex += cw_->flex;
        } else {
            /* No flex, no fixed: measure current window size as default. */
            RECT wr;
            GetWindowRect(cw_->hwnd, &wr);
            int nat = is_vert ? (wr.bottom - wr.top) : (wr.right - wr.left);
            if (nat < 1) nat = is_vert ? 24 : 80; /* reasonable fallback */
            sizes[i] = nat;
            fixed_used += nat;
        }
        if (i < n - 1) fixed_used += sp; /* spacing between children */
    }

    /* Available space for flex children. */
    int avail = (is_vert ? (total_h - pt - pb) : (total_w - pl - pr)) - fixed_used;
    if (avail < 0) avail = 0;

    /* Second pass: assign flex sizes. */
    if (total_flex > 0) {
        for (int i = 0; i < n; i++) {
            ElWidget* cw_ = el_widget_get(pw->children[i]);
            if (!cw_ || !cw_->hwnd) continue;
            if (cw_->flex > 0 && sizes[i] == 0) {
                sizes[i] = (avail * cw_->flex) / total_flex;
            }
        }
    }

    /* Third pass: position children. */
    int cursor = is_vert ? pt : pl;
    for (int i = 0; i < n; i++) {
        ElWidget* cw_ = el_widget_get(pw->children[i]);
        if (!cw_ || !cw_->hwnd) continue;

        int x, y, cw, ch;
        if (is_vert) {
            x  = pl;
            y  = cursor;
            cw = (cw_->fixed_w > 0) ? cw_->fixed_w : (total_w - pl - pr);
            ch = sizes[i];
            if (cw < 0) cw = 0;
            if (ch < 0) ch = 0;
            cursor += ch + sp;
        } else {
            x  = cursor;
            y  = pt;
            cw = sizes[i];
            ch = (cw_->fixed_h > 0) ? cw_->fixed_h : (total_h - pt - pb);
            if (cw < 0) cw = 0;
            if (ch < 0) ch = 0;
            cursor += cw + sp;
        }

        SetWindowPos(cw_->hwnd, NULL, x, y, cw, ch,
                     SWP_NOZORDER | SWP_NOACTIVATE);

        /* Recursively reflow child containers. */
        if (cw_->kind == EL_WIDGET_VSTACK ||
            cw_->kind == EL_WIDGET_HSTACK ||
            cw_->kind == EL_WIDGET_ZSTACK ||
            cw_->kind == EL_WIDGET_SCROLL ||
            cw_->kind == EL_WIDGET_WINDOW) {
            el_win32_reflow(pw->children[i]);
        }
    }

    free(sizes);
}

/* ── Container WndProc ────────────────────────────────────────────────────── */
/*
 * Used for VSTACK, HSTACK, ZSTACK, SCROLL containers.
 * Handles WM_SIZE (reflow), WM_CTLCOLOR* (color theming), WM_COMMAND (events),
 * WM_VSCROLL (scroll container).
 */
static LRESULT CALLBACK el_container_wndproc(HWND hwnd, UINT msg,
                                               WPARAM wp, LPARAM lp) {
    int64_t slot = el_hwnd_to_slot(hwnd);
    ElWidget* w  = (slot >= 0) ? el_widget_get(slot) : NULL;

    switch (msg) {
    case WM_SIZE:
        if (w) el_win32_reflow(slot);
        return 0;

    case WM_EL_REFLOW:
        if (w) el_win32_reflow(slot);
        return 0;

    case WM_VSCROLL: {
        if (!w || w->kind != EL_WIDGET_SCROLL) break;
        SCROLLINFO si;
        si.cbSize = sizeof(si);
        si.fMask  = SIF_ALL;
        GetScrollInfo(hwnd, SB_VERT, &si);
        int pos = si.nPos;
        switch (LOWORD(wp)) {
            case SB_LINEUP:        pos -= 20;      break;
            case SB_LINEDOWN:      pos += 20;      break;
            case SB_PAGEUP:        pos -= si.nPage; break;
            case SB_PAGEDOWN:      pos += si.nPage; break;
            case SB_THUMBTRACK:    pos  = si.nTrackPos; break;
            case SB_TOP:           pos  = si.nMin;  break;
            case SB_BOTTOM:        pos  = si.nMax;  break;
        }
        if (pos < si.nMin) pos = si.nMin;
        if (pos > si.nMax - (int)si.nPage) pos = si.nMax - (int)si.nPage;
        si.fMask = SIF_POS;
        si.nPos  = pos;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
        /* Scroll child. */
        if (w->child_count > 0) {
            ElWidget* cw_ = el_widget_get(w->children[0]);
            if (cw_ && cw_->hwnd) {
                RECT cr; GetClientRect(hwnd, &cr);
                SetWindowPos(cw_->hwnd, NULL, 0, -pos,
                             cr.right - cr.left, 0,
                             SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
            }
        }
        return 0;
    }

    case WM_COMMAND: {
        /* Route click / change notifications to el callbacks. */
        HWND ctrl = (HWND)lp;
        if (!ctrl) break;
        int64_t cs = el_hwnd_to_slot(ctrl);
        ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL;
        if (!cw) break;

        WORD notif = HIWORD(wp);
        if (cw->kind == EL_WIDGET_BUTTON && notif == BN_CLICKED) {
            el_win32_invoke_cb(cw->cb_click, cs, "");
        } else if (cw->kind == EL_WIDGET_TEXTFIELD) {
            if (notif == EN_CHANGE) {
                el_win32_invoke_cb(cw->cb_change, cs, "");
            }
        } else if (cw->kind == EL_WIDGET_TEXTAREA) {
            if (notif == EN_CHANGE) {
                el_win32_invoke_cb(cw->cb_change, cs, "");
            }
        }
        /* Propagate to parent so top-level window also sees it. */
        HWND parent = GetParent(hwnd);
        if (parent) SendMessageW(parent, msg, wp, lp);
        return 0;
    }

    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORBTN: {
        HWND ctrl = (HWND)lp;
        int64_t cs = el_hwnd_to_slot(ctrl);
        ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL;
        if (cw) {
            HDC hdc = (HDC)wp;
            if (cw->has_fg) SetTextColor(hdc, cw->fg_color);
            if (cw->has_bg) {
                SetBkColor(hdc, cw->bg_color);
                if (cw->bg_brush) return (LRESULT)cw->bg_brush;
            }
        }
        break;
    }

    case WM_ERASEBKGND: {
        if (w && w->has_bg && w->bg_brush) {
            HDC hdc = (HDC)wp;
            RECT rc;
            GetClientRect(hwnd, &rc);
            FillRect(hdc, &rc, w->bg_brush);
            return 1;
        }
        break;
    }

    case WM_DESTROY:
        return 0;
    }

    return DefWindowProcW(hwnd, msg, wp, lp);
}

/* ── Top-level window WndProc ────────────────────────────────────────────── */
static LRESULT CALLBACK el_toplevel_wndproc(HWND hwnd, UINT msg,
                                              WPARAM wp, LPARAM lp) {
    int64_t slot = el_hwnd_to_slot(hwnd);
    ElWidget* w  = (slot >= 0) ? el_widget_get(slot) : NULL;

    switch (msg) {
    case WM_SIZE:
        if (w) el_win32_reflow(slot);
        return 0;

    case WM_EL_REFLOW:
        if (w) el_win32_reflow(slot);
        return 0;

    case WM_COMMAND: {
        /* Bubble COMMAND messages from child controls. */
        HWND ctrl = (HWND)lp;
        if (!ctrl) break;
        int64_t cs = el_hwnd_to_slot(ctrl);
        ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL;
        if (!cw) {
            /* Not a direct slot child — pass to DefWindowProc. */
            break;
        }
        WORD notif = HIWORD(wp);
        if (cw->kind == EL_WIDGET_BUTTON && notif == BN_CLICKED) {
            el_win32_invoke_cb(cw->cb_click, cs, "");
        } else if ((cw->kind == EL_WIDGET_TEXTFIELD ||
                    cw->kind == EL_WIDGET_TEXTAREA) && notif == EN_CHANGE) {
            el_win32_invoke_cb(cw->cb_change, cs, "");
        }
        return 0;
    }

    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORBTN: {
        HWND ctrl = (HWND)lp;
        int64_t cs = el_hwnd_to_slot(ctrl);
        ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL;
        if (cw) {
            HDC hdc = (HDC)wp;
            if (cw->has_fg) SetTextColor(hdc, cw->fg_color);
            if (cw->has_bg) {
                SetBkColor(hdc, cw->bg_color);
                if (cw->bg_brush) return (LRESULT)cw->bg_brush;
            }
        }
        break;
    }

    case WM_ERASEBKGND: {
        if (w && w->has_bg && w->bg_brush) {
            HDC hdc = (HDC)wp;
            RECT rc;
            GetClientRect(hwnd, &rc);
            FillRect(hdc, &rc, w->bg_brush);
            return 1;
        }
        break;
    }

    case WM_GETMINMAXINFO: {
        /* Stored in the window's GWLP_USERDATA as packed two ints: min_w | min_h. */
        LONG_PTR ud = GetWindowLongPtrW(hwnd, GWLP_USERDATA);
        if (ud) {
            int min_w = (int)(ud & 0xFFFF);
            int min_h = (int)((ud >> 16) & 0xFFFF);
            MINMAXINFO* mm = (MINMAXINFO*)lp;
            if (min_w > 0) mm->ptMinTrackSize.x = min_w;
            if (min_h > 0) mm->ptMinTrackSize.y = min_h;
        }
        return 0;
    }

    case WM_CLOSE:
        DestroyWindow(hwnd);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(hwnd, msg, wp, lp);
}

/* ── Subclass proc for EDIT controls (Enter key → on_submit) ────────────── */
static LRESULT CALLBACK el_edit_subclass_proc(HWND hwnd, UINT msg, WPARAM wp,
                                               LPARAM lp, UINT_PTR uid,
                                               DWORD_PTR ref_data) {
    (void)uid; (void)ref_data;
    if (msg == WM_KEYDOWN && wp == VK_RETURN) {
        int64_t slot = el_hwnd_to_slot(hwnd);
        ElWidget* w  = (slot >= 0) ? el_widget_get(slot) : NULL;
        if (w && w->cb_click) {
            /* Gather current text as data. */
            int len = GetWindowTextLengthW(hwnd);
            WCHAR* wbuf = (WCHAR*)malloc((size_t)(len + 2) * sizeof(WCHAR));
            if (wbuf) {
                GetWindowTextW(hwnd, wbuf, len + 1);
                char* utf8 = el_wide_to_utf8(wbuf);
                free(wbuf);
                el_win32_invoke_cb(w->cb_click, slot, utf8);
                free(utf8);
            }
            return 0; /* suppress default beep */
        }
    }
    return DefSubclassProc(hwnd, msg, wp, lp);
}

/* ── Window-class registration ────────────────────────────────────────────── */
static int _el_classes_registered = 0;

static void el_win32_register_classes(void) {
    if (_el_classes_registered) return;
    _el_classes_registered = 1;

    /* Initialize common controls (needed for SetWindowSubclass). */
    INITCOMMONCONTROLSEX icc;
    icc.dwSize = sizeof(icc);
    icc.dwICC  = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES;
    InitCommonControlsEx(&icc);

    HINSTANCE hinst = GetModuleHandleW(NULL);

    /* Container class (VStack, HStack, ZStack, Scroll) */
    WNDCLASSW wc;
    memset(&wc, 0, sizeof(wc));
    wc.lpfnWndProc   = el_container_wndproc;
    wc.hInstance     = hinst;
    wc.hCursor       = LoadCursorW(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = EL_WNDCLASS_VSTACK;
    RegisterClassW(&wc);

    wc.lpszClassName = EL_WNDCLASS_HSTACK;
    RegisterClassW(&wc);

    wc.lpszClassName = EL_WNDCLASS_ZSTACK;
    RegisterClassW(&wc);

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpszClassName = EL_WNDCLASS_SCROLL;
    RegisterClassW(&wc);

    /* Top-level window class */
    memset(&wc, 0, sizeof(wc));
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = el_toplevel_wndproc;
    wc.hInstance     = hinst;
    wc.hCursor       = LoadCursorW(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = EL_WNDCLASS_WINDOW;
    RegisterClassW(&wc);
}

/* ── Public C API ─────────────────────────────────────────────────────────── */

/*
 * el_win32_init — one-time initialisation. Must be called before any other
 * el_win32_* function.
 */
void el_win32_init(void) {
    static int done = 0;
    if (done) return;
    done = 1;
    el_win32_register_classes();
}

/*
 * el_win32_run_loop — enter the Win32 message loop. Never returns.
 * Must be called from the main thread after all windows are shown.
 */
void el_win32_run_loop(void) {
    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
}

/* ── Window ───────────────────────────────────────────────────────────────── */

int64_t el_win32_window_create(const char* title, int width, int height,
                                int min_width, int min_height) {
    el_win32_register_classes();
    HINSTANCE hinst = GetModuleHandleW(NULL);
    WCHAR* wtitle = el_utf8_to_wide(title ? title : "");

    DWORD style = WS_OVERLAPPEDWINDOW;
    HWND hwnd = CreateWindowW(
        EL_WNDCLASS_WINDOW,
        wtitle,
        style,
        CW_USEDEFAULT, CW_USEDEFAULT,
        width, height,
        NULL, NULL, hinst, NULL);

    free(wtitle);
    if (!hwnd) return -1;

    /* Pack min_width / min_height into GWLP_USERDATA (low 16 = min_w, next 16 = min_h). */
    LONG_PTR ud = ((LONG_PTR)(min_height & 0xFFFF) << 16) | (LONG_PTR)(min_width & 0xFFFF);
    SetWindowLongPtrW(hwnd, GWLP_USERDATA, ud);

    int64_t handle = el_widget_alloc(EL_WIDGET_WINDOW, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }

    ElWidget* w = el_widget_get(handle);
    w->spacing = 0;

    return handle;
}

void el_win32_window_show(int64_t handle) {
    ElWidget* w = el_widget_get(handle);
    if (!w || w->kind != EL_WIDGET_WINDOW || !w->hwnd) return;
    ShowWindow(w->hwnd, SW_SHOW);
    UpdateWindow(w->hwnd);
}

void el_win32_window_set_title(int64_t handle, const char* title) {
    ElWidget* w = el_widget_get(handle);
    if (!w || w->kind != EL_WIDGET_WINDOW || !w->hwnd) return;
    WCHAR* wt = el_utf8_to_wide(title ? title : "");
    SetWindowTextW(w->hwnd, wt);
    free(wt);
}

/* ── Layout container factories ───────────────────────────────────────────── */

static int64_t el_win32_make_container(const WCHAR* class_name, ElWidgetKind kind,
                                        int spacing) {
    el_win32_register_classes();
    HWND hwnd = CreateWindowW(
        class_name,
        L"",
        WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
        0, 0, 0, 0,
        HWND_MESSAGE, /* temporary parent; reparented on add_child */
        NULL,
        GetModuleHandleW(NULL),
        NULL);
    if (!hwnd) return -1;

    int64_t handle = el_widget_alloc(kind, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }

    ElWidget* w = el_widget_get(handle);
    w->spacing  = spacing;
    return handle;
}

int64_t el_win32_vstack_create(int spacing) {
    return el_win32_make_container(EL_WNDCLASS_VSTACK, EL_WIDGET_VSTACK, spacing);
}

int64_t el_win32_hstack_create(int spacing) {
    return el_win32_make_container(EL_WNDCLASS_HSTACK, EL_WIDGET_HSTACK, spacing);
}

int64_t el_win32_zstack_create(void) {
    return el_win32_make_container(EL_WNDCLASS_ZSTACK, EL_WIDGET_ZSTACK, 0);
}

int64_t el_win32_scroll_create(void) {
    HWND hwnd = CreateWindowW(
        EL_WNDCLASS_SCROLL,
        L"",
        WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_VSCROLL,
        0, 0, 0, 0,
        HWND_MESSAGE,
        NULL,
        GetModuleHandleW(NULL),
        NULL);
    if (!hwnd) return -1;

    int64_t handle = el_widget_alloc(EL_WIDGET_SCROLL, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }
    return handle;
}

/* ── Widget factories ─────────────────────────────────────────────────────── */

int64_t el_win32_label_create(const char* text) {
    el_win32_register_classes();
    WCHAR* wt = el_utf8_to_wide(text ? text : "");
    HWND hwnd = CreateWindowW(
        L"STATIC",
        wt,
        WS_CHILD | WS_VISIBLE | SS_LEFT,
        0, 0, 0, 24,
        HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
    free(wt);
    if (!hwnd) return -1;
    return el_widget_alloc(EL_WIDGET_LABEL, hwnd);
}

int64_t el_win32_button_create(const char* label) {
    el_win32_register_classes();
    WCHAR* wl = el_utf8_to_wide(label ? label : "");
    HWND hwnd = CreateWindowW(
        L"BUTTON",
        wl,
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        0, 0, 0, 32,
        HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
    free(wl);
    if (!hwnd) return -1;
    return el_widget_alloc(EL_WIDGET_BUTTON, hwnd);
}

int64_t el_win32_text_field_create(const char* placeholder) {
    el_win32_register_classes();
    HWND hwnd = CreateWindowW(
        L"EDIT",
        L"",
        WS_CHILD | WS_VISIBLE | WS_BORDER |
        ES_AUTOHSCROLL | ES_LEFT,
        0, 0, 0, 26,
        HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
    if (!hwnd) return -1;

    /* Set placeholder / cue banner (Windows Vista+). */
    if (placeholder && *placeholder) {
        WCHAR* wp = el_utf8_to_wide(placeholder);
        SendMessageW(hwnd, EM_SETCUEBANNER, TRUE, (LPARAM)wp);
        free(wp);
    }

    int64_t handle = el_widget_alloc(EL_WIDGET_TEXTFIELD, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }

    /* Subclass to intercept VK_RETURN for on_submit. */
    SetWindowSubclass(hwnd, el_edit_subclass_proc, (UINT_PTR)handle, 0);
    return handle;
}

int64_t el_win32_text_area_create(const char* placeholder) {
    el_win32_register_classes();
    HWND hwnd = CreateWindowW(
        L"EDIT",
        L"",
        WS_CHILD | WS_VISIBLE | WS_BORDER |
        ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL,
        0, 0, 0, 80,
        HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
    if (!hwnd) return -1;

    /* EDIT multiline does not support EM_SETCUEBANNER, so no placeholder. */
    (void)placeholder;

    int64_t handle = el_widget_alloc(EL_WIDGET_TEXTAREA, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }
    return handle;
}

int64_t el_win32_image_create(const char* path) {
    el_win32_register_classes();
    HWND hwnd = CreateWindowW(
        L"STATIC",
        L"",
        WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_REALSIZECONTROL,
        0, 0, 0, 0,
        HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL);
    if (!hwnd) return -1;

    int64_t handle = el_widget_alloc(EL_WIDGET_IMAGE, hwnd);
    if (handle < 0) { DestroyWindow(hwnd); return -1; }

    ElWidget* w = el_widget_get(handle);

    if (path && *path) {
        WCHAR* wpath = el_utf8_to_wide(path);
        HBITMAP hbmp = (HBITMAP)LoadImageW(NULL, wpath, IMAGE_BITMAP,
                                            0, 0,
                                            LR_LOADFROMFILE | LR_DEFAULTSIZE);
        free(wpath);
        if (hbmp) {
            w->hbmp = hbmp;
            SendMessageW(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp);
        }
    }

    return handle;
}

/* ── Widget property setters ─────────────────────────────────────────────── */

void el_win32_widget_set_text(int64_t handle, const char* text) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    WCHAR* wt = el_utf8_to_wide(text ? text : "");
    SetWindowTextW(w->hwnd, wt);
    free(wt);
}

const char* el_win32_widget_get_text(int64_t handle) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return strdup("");
    int len = GetWindowTextLengthW(w->hwnd);
    if (len <= 0) return strdup("");
    WCHAR* wbuf = (WCHAR*)malloc((size_t)(len + 2) * sizeof(WCHAR));
    if (!wbuf) return strdup("");
    GetWindowTextW(w->hwnd, wbuf, len + 1);
    char* result = el_wide_to_utf8(wbuf);
    free(wbuf);
    return result; /* caller must free() */
}

void el_win32_widget_set_color(int64_t handle, float r, float g, float b, float a) {
    (void)a; /* Win32 classic does not support per-control alpha */
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    w->fg_color = RGB((int)(r * 255.0f), (int)(g * 255.0f), (int)(b * 255.0f));
    w->has_fg   = 1;
    InvalidateRect(w->hwnd, NULL, TRUE);
}

void el_win32_widget_set_bg_color(int64_t handle, float r, float g, float b, float a) {
    (void)a;
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    w->bg_color = RGB((int)(r * 255.0f), (int)(g * 255.0f), (int)(b * 255.0f));
    w->has_bg   = 1;
    if (w->bg_brush) DeleteObject(w->bg_brush);
    w->bg_brush = CreateSolidBrush(w->bg_color);
    InvalidateRect(w->hwnd, NULL, TRUE);
}

void el_win32_widget_set_font(int64_t handle, const char* family, int size, int bold) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;

    LOGFONTW lf;
    memset(&lf, 0, sizeof(lf));
    lf.lfHeight  = -MulDiv(size > 0 ? size : 12, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72);
    lf.lfWeight  = bold ? FW_BOLD : FW_NORMAL;
    lf.lfCharSet = DEFAULT_CHARSET;

    if (family && *family && strcmp(family, "system") != 0) {
        WCHAR* wf = el_utf8_to_wide(family);
        wcsncpy(lf.lfFaceName, wf, LF_FACESIZE - 1);
        free(wf);
    }
    /* family == NULL or "system" → lfFaceName stays zeroed → system font */

    HFONT hfont = CreateFontIndirectW(&lf);
    if (!hfont) return;

    if (w->font) DeleteObject(w->font);
    w->font = hfont;
    SendMessageW(w->hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
}

void el_win32_widget_set_padding(int64_t handle, int top, int right,
                                  int bottom, int left) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    w->pad_top    = top;
    w->pad_right  = right;
    w->pad_bottom = bottom;
    w->pad_left   = left;
    /* Trigger reflow if already parented. */
    if (w->hwnd) PostMessageW(w->hwnd, WM_EL_REFLOW, 0, 0);
}

void el_win32_widget_set_width(int64_t handle, int width) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    w->fixed_w = width;
    if (w->hwnd) {
        RECT r; GetWindowRect(w->hwnd, &r);
        int h = r.bottom - r.top;
        SetWindowPos(w->hwnd, NULL, 0, 0, width, h,
                     SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

void el_win32_widget_set_height(int64_t handle, int height) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    w->fixed_h = height;
    if (w->hwnd) {
        RECT r; GetWindowRect(w->hwnd, &r);
        int cw = r.right - r.left;
        SetWindowPos(w->hwnd, NULL, 0, 0, cw, height,
                     SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

void el_win32_widget_set_flex(int64_t handle, int flex) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    w->flex = flex;
    /* Let parent reflow absorb the change. */
}

void el_win32_widget_set_corner_radius(int64_t handle, int radius) {
    /*
     * Classic Win32 does not have native per-control corner radii.
     * If radius > 0 we apply a rounded rectangular window region so the
     * control at least clips to a rounded rect. This is approximate — it
     * does not anti-alias and may look boxy on high-DPI displays.
     */
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd || radius <= 0) return;
    RECT r; GetWindowRect(w->hwnd, &r);
    int ww = r.right  - r.left;
    int wh = r.bottom - r.top;
    if (ww <= 0 || wh <= 0) return;
    HRGN rgn = CreateRoundRectRgn(0, 0, ww, wh, radius * 2, radius * 2);
    SetWindowRgn(w->hwnd, rgn, TRUE);
    /* Note: SetWindowRgn takes ownership of rgn; do NOT DeleteObject it. */
}

void el_win32_widget_set_disabled(int64_t handle, int disabled) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    EnableWindow(w->hwnd, disabled ? FALSE : TRUE);
}

void el_win32_widget_set_hidden(int64_t handle, int hidden) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    ShowWindow(w->hwnd, hidden ? SW_HIDE : SW_SHOW);
}

/* ── Child management ─────────────────────────────────────────────────────── */

/*
 * el_win32_widget_add_child — attach child widget to parent.
 *
 * - WINDOW / VSTACK / HSTACK / ZSTACK: SetParent child into container.
 * - SCROLL: first child becomes the scrolled content.
 *
 * After reparenting, triggers a layout reflow of the parent.
 */
void el_win32_widget_add_child(int64_t parent, int64_t child) {
    ElWidget* pw = el_widget_get(parent);
    ElWidget* cw = el_widget_get(child);
    if (!pw || !cw || !pw->hwnd || !cw->hwnd) return;
    if (cw->kind == EL_WIDGET_WINDOW) return; /* cannot add window as child */
    if (pw->child_count >= EL_WIN32_MAX_CHILDREN) return;

    /* Reparent the child HWND into the container. */
    SetParent(cw->hwnd, pw->hwnd);

    /* Make sure the WS_CHILD style is set and WS_POPUP is cleared. */
    LONG_PTR style = GetWindowLongPtrW(cw->hwnd, GWL_STYLE);
    style |=  WS_CHILD;
    style &= ~WS_POPUP;
    SetWindowLongPtrW(cw->hwnd, GWL_STYLE, style);
    ShowWindow(cw->hwnd, SW_SHOW);

    pw->children[pw->child_count++] = child;

    /* Reflow parent so the new child gets positioned. */
    el_win32_reflow(parent);
}

void el_win32_widget_remove_child(int64_t parent, int64_t child) {
    ElWidget* pw = el_widget_get(parent);
    ElWidget* cw = el_widget_get(child);
    if (!cw || !cw->hwnd) return;

    /* Detach HWND from parent. */
    SetParent(cw->hwnd, NULL);

    /* Remove from parent's child list. */
    if (pw) {
        for (int i = 0; i < pw->child_count; i++) {
            if (pw->children[i] == child) {
                memmove(&pw->children[i], &pw->children[i + 1],
                        (size_t)(pw->child_count - i - 1) * sizeof(int64_t));
                pw->child_count--;
                break;
            }
        }
        el_win32_reflow(parent);
    }
}

/* ── Event registration ───────────────────────────────────────────────────── */

void el_win32_widget_on_click(int64_t handle, const char* fn_name) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_click);
    w->cb_click = (fn_name && *fn_name) ? strdup(fn_name) : NULL;
}

void el_win32_widget_on_change(int64_t handle, const char* fn_name) {
    ElWidget* w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_change);
    w->cb_change = (fn_name && *fn_name) ? strdup(fn_name) : NULL;
}

void el_win32_widget_on_submit(int64_t handle, const char* fn_name) {
    /* For text fields, submit (Enter key) is stored in cb_click, same as AppKit. */
    el_win32_widget_on_click(handle, fn_name);
}

/* ── Widget destroy ───────────────────────────────────────────────────────── */

void el_win32_widget_destroy(int64_t handle) {
    ElWidget* w = el_widget_get(handle);
    if (!w || !w->hwnd) return;
    DestroyWindow(w->hwnd);
    el_widget_free_slot(handle);
}

/* ── Manifest reader ──────────────────────────────────────────────────────── */
/*
 * Same parser as the macOS implementation in el_seed.c.
 * Reads the app{} block from a manifest.el file:
 *
 *   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 failure.
 *
 * NOTE: el_seed.c already defines __manifest_read for both EL_TARGET_MACOS and
 * EL_TARGET_WIN32 using the same parser body. We re-implement it here because
 * el_seed.c's implementation is inside #ifdef EL_TARGET_MACOS; the Win32
 * version lives here, symmetric with the macOS one.
 */

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

static char* w32_manifest_read_string(const char** p) {
    w32_manifest_skip_ws(p);
    if (**p != '"') return NULL;
    (*p)++;
    const char* start = *p;
    while (**p && **p != '"') {
        if (**p == '\\') (*p)++;
        (*p)++;
    }
    size_t len = (size_t)(*p - start);
    if (**p == '"') (*p)++;
    char* out = (char*)malloc(len + 1);
    if (!out) return NULL;
    memcpy(out, start, len);
    out[len] = '\0';
    return out;
}

static int64_t w32_manifest_read_int(const char** p) {
    w32_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;
}

static const char* w32_manifest_find_app_block(const char* src) {
    while (*src) {
        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 helpers — mirror of what el_seed.c provides. */
#ifndef EL_CSTR
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
#endif
#ifndef EL_STR
#define EL_STR(p)  ((el_val_t)(uintptr_t)(p))
#endif

el_val_t el_win32_manifest_read(const char* path) {
    if (!path || !*path) return EL_STR(strdup("{}"));

    FILE* f = fopen(path, "r");
    if (!f) return EL_STR(strdup("{}"));
    fseek(f, 0, SEEK_END);
    long sz = ftell(f);
    rewind(f);
    if (sz <= 0) { fclose(f); return EL_STR(strdup("{}")); }
    char* src = (char*)malloc((size_t)sz + 1);
    if (!src) { fclose(f); return EL_STR(strdup("{}")); }
    size_t got = fread(src, 1, (size_t)sz, f);
    src[got] = '\0';
    fclose(f);

    const char* app_start = w32_manifest_find_app_block(src);
    if (!app_start) { free(src); return EL_STR(strdup("{}")); }

    char*   title  = NULL;
    int64_t width  = 1200, height = 800;
    int64_t min_w  = 600,  min_h  = 400;

    const char* cur = app_start;
    while (*cur && *cur != '}') {
        while (*cur && (unsigned char)*cur <= ' ') cur++;
        if (*cur == '}') break;
        if (cur[0] == '/' && cur[1] == '/') {
            while (*cur && *cur != '\n') cur++;
            continue;
        }
        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; }
        while (*cur == ' ' || *cur == '\t') cur++;

        if      (key_len == 12 && strncmp(key_start, "window_title",  12) == 0) { free(title); title  = w32_manifest_read_string(&cur); }
        else if (key_len == 12 && strncmp(key_start, "window_width",  12) == 0) { width  = w32_manifest_read_int(&cur); }
        else if (key_len == 13 && strncmp(key_start, "window_height", 13) == 0) { height = w32_manifest_read_int(&cur); }
        else if (key_len ==  9 && strncmp(key_start, "min_width",      9) == 0) { min_w  = w32_manifest_read_int(&cur); }
        else if (key_len == 10 && strncmp(key_start, "min_height",    10) == 0) { min_h  = w32_manifest_read_int(&cur); }
        else { while (*cur && *cur != '\n' && *cur != '}') cur++; }
    }

    free(src);

    const char* t = title ? title : "App";
    size_t buf_sz = strlen(t) * 6 + 128;
    char* buf = (char*)malloc(buf_sz);
    if (!buf) { free(title); return EL_STR(strdup("{}")); }
    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 EL_STR(buf);
}

/* ── el_seed.c __* builtins for EL_TARGET_WIN32 ──────────────────────────── */
/*
 * These are the thin el_val_t wrappers that el_seed.c (with EL_TARGET_WIN32)
 * will call. They mirror the EL_TARGET_MACOS section in el_seed.c exactly.
 *
 * el_seed.c includes this file's declarations via el_native_target.h and
 * links el_win32.o. The __* symbol definitions below are compiled into
 * el_win32.c itself (not el_seed.c) when EL_TARGET_WIN32 is defined, keeping
 * el_seed.c platform-agnostic.
 */

/* Helper: extract el string pointer. */
static inline const char* _w32_cstr(el_val_t v) {
    return (const char*)(uintptr_t)v;
}
static inline el_val_t _w32_str(const char* p) {
    return (el_val_t)(uintptr_t)p;
}

/* Bit-cast helper for float args (el_val_t carries IEEE 754 doubles). */
static inline double _w32_to_float(el_val_t v) {
    union { double d; int64_t i; } u; u.i = (int64_t)v; return u.d;
}

void     __native_init(void)     { el_win32_init(); }
void     __native_run_loop(void) { el_win32_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_win32_window_create(
        _w32_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_win32_window_show((int64_t)h); }
void __window_set_title(el_val_t h, el_val_t t) { el_win32_window_set_title((int64_t)h, _w32_cstr(t)); }

el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_win32_vstack_create((int)(int64_t)spacing); }
el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_win32_hstack_create((int)(int64_t)spacing); }
el_val_t __zstack_create(void)             { return (el_val_t)el_win32_zstack_create(); }
el_val_t __scroll_create(void)             { return (el_val_t)el_win32_scroll_create(); }

el_val_t __label_create(el_val_t text)             { return (el_val_t)el_win32_label_create(_w32_cstr(text)); }
el_val_t __button_create(el_val_t label)           { return (el_val_t)el_win32_button_create(_w32_cstr(label)); }
el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_win32_text_field_create(_w32_cstr(placeholder)); }
el_val_t __text_area_create(el_val_t placeholder)  { return (el_val_t)el_win32_text_area_create(_w32_cstr(placeholder)); }
el_val_t __image_create(el_val_t path)             { return (el_val_t)el_win32_image_create(_w32_cstr(path)); }

void __widget_set_text(el_val_t h, el_val_t t)    { el_win32_widget_set_text((int64_t)h, _w32_cstr(t)); }

el_val_t __widget_get_text(el_val_t h) {
    const char* s = el_win32_widget_get_text((int64_t)h);
    /* el_win32_widget_get_text returns a malloc'd string; caller treats as
     * short-lived (same contract as el_appkit version). */
    return s ? _w32_str(s) : _w32_str(strdup(""));
}

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_win32_widget_set_color((int64_t)h,
        (float)_w32_to_float(r), (float)_w32_to_float(g),
        (float)_w32_to_float(b), (float)_w32_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_win32_widget_set_bg_color((int64_t)h,
        (float)_w32_to_float(r), (float)_w32_to_float(g),
        (float)_w32_to_float(b), (float)_w32_to_float(a));
}

void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) {
    el_win32_widget_set_font((int64_t)h, _w32_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_win32_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_win32_widget_set_width((int64_t)h, (int)(int64_t)w); }
void __widget_set_height(el_val_t h, el_val_t ht)       { el_win32_widget_set_height((int64_t)h, (int)(int64_t)ht); }
void __widget_set_flex(el_val_t h, el_val_t f)          { el_win32_widget_set_flex((int64_t)h, (int)(int64_t)f); }
void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_win32_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); }
void __widget_set_disabled(el_val_t h, el_val_t d)      { el_win32_widget_set_disabled((int64_t)h, (int)(int64_t)d); }
void __widget_set_hidden(el_val_t h, el_val_t hid)      { el_win32_widget_set_hidden((int64_t)h, (int)(int64_t)hid); }

void __widget_add_child(el_val_t p, el_val_t c)   { el_win32_widget_add_child((int64_t)p, (int64_t)c); }
void __widget_remove_child(el_val_t p, el_val_t c) { el_win32_widget_remove_child((int64_t)p, (int64_t)c); }
void __widget_destroy(el_val_t h)                  { el_win32_widget_destroy((int64_t)h); }

void __widget_on_click(el_val_t h, el_val_t fn_name)   { el_win32_widget_on_click((int64_t)h, _w32_cstr(fn_name)); }
void __widget_on_change(el_val_t h, el_val_t fn_name)  { el_win32_widget_on_change((int64_t)h, _w32_cstr(fn_name)); }
void __widget_on_submit(el_val_t h, el_val_t fn_name)  { el_win32_widget_on_submit((int64_t)h, _w32_cstr(fn_name)); }

el_val_t __manifest_read(el_val_t path) {
    return el_win32_manifest_read(_w32_cstr(path));
}

#endif /* EL_TARGET_WIN32 */
