#!/usr/bin/env node
// _pre-screen-worker.js — Child process that loads WASM kernel + HS modules
// and tests a batch of hyperscript sources for compilation hangs.
//
// Input: HS_BATCH_FILE env var points to a JSON array of source strings.
// Output: One line per source: RESULT:{"source":"...","status":"ok|error|hang"}
//
// Each source gets a 2-second timeout via a per-source alarm.

const fs = require('fs');
const path = require('path');

const PROJECT_ROOT = process.cwd();
const WASM_DIR = path.join(PROJECT_ROOT, 'shared/static/wasm');

// --- Minimal DOM stubs (same as test_hs_repeat.js) ---
function makeElement(tag) {
  const el = {
    tagName: tag, _attrs: {}, _children: [], _classes: new Set(),
    style: {}, childNodes: [], children: [], textContent: '',
    nodeType: 1,
    classList: {
      add(c) { el._classes.add(c); },
      remove(c) { el._classes.delete(c); },
      contains(c) { return el._classes.has(c); },
      toggle(c) { if (el._classes.has(c)) el._classes.delete(c); else el._classes.add(c); },
    },
    setAttribute(k, v) { el._attrs[k] = String(v); },
    getAttribute(k) { return el._attrs[k] || null; },
    removeAttribute(k) { delete el._attrs[k]; },
    appendChild(c) { el._children.push(c); return c; },
    insertBefore(c) { el._children.push(c); return c; },
    removeChild(c) { return c; },
    replaceChild(n) { return n; },
    cloneNode() { return makeElement(tag); },
    get innerHTML() { return ''; },
    set innerHTML(v) { el._children = []; },
    get outerHTML() { return `<${tag}>`; },
    dataset: new Proxy({}, {
      get(_, k) { return el._attrs['data-' + k.replace(/[A-Z]/g, c => '-' + c.toLowerCase())]; },
      set(_, k, v) { el._attrs['data-' + k.replace(/[A-Z]/g, c => '-' + c.toLowerCase())] = v; return true; }
    }),
    querySelectorAll() { return []; },
    querySelector() { return null; },
    addEventListener() {}, removeEventListener() {}, dispatchEvent() {},
  };
  return el;
}

global.window = global;
global.document = {
  createElement: makeElement,
  createDocumentFragment() { return makeElement('fragment'); },
  head: makeElement('head'), body: makeElement('body'),
  querySelector() { return null; }, querySelectorAll() { return []; },
  createTextNode(s) { return { _isText: true, textContent: String(s), nodeType: 3 }; },
  addEventListener() {},
  createComment(s) { return { _isComment: true, textContent: s || '', nodeType: 8 }; },
  getElementsByTagName() { return []; },
};
global.localStorage = { getItem() { return null; }, setItem() {}, removeItem() {} };
global.CustomEvent = class { constructor(n, o) { this.type = n; this.detail = (o || {}).detail || {}; } };
global.MutationObserver = class { observe() {} disconnect() {} };
global.requestIdleCallback = fn => setTimeout(fn, 0);
global.matchMedia = () => ({ matches: false });
global.navigator = { serviceWorker: { register() { return Promise.resolve(); } } };
global.location = { href: '', pathname: '/', hostname: 'localhost' };
global.history = { pushState() {}, replaceState() {} };
global.fetch = () => Promise.resolve({ ok: true, text() { return Promise.resolve(''); } });
global.XMLHttpRequest = class { open() {} send() {} };

// Load WASM kernel
require(path.join(WASM_DIR, 'sx_browser.bc.js'));
const K = globalThis.SxKernel;
if (!K) { console.error('FATAL: SxKernel not found'); process.exit(1); }

// Register FFI primitives
K.registerNative('host-global', args => {
  const name = args[0];
  return (name in globalThis) ? globalThis[name] : null;
});
K.registerNative('host-get', args => {
  const [obj, prop] = args;
  if (obj == null) return null;
  const v = obj[prop];
  return v === undefined ? null : v;
});
K.registerNative('host-set!', args => { if (args[0] != null) args[0][args[1]] = args[2]; return args[2]; });
K.registerNative('host-call', args => {
  const [obj, method, ...rest] = args;
  if (obj == null || typeof obj[method] !== 'function') return null;
  const r = obj[method].apply(obj, rest);
  return r === undefined ? null : r;
});
K.registerNative('host-new', args => new (Function.prototype.bind.apply(args[0], [null, ...args.slice(1)])));
K.registerNative('host-callback', args => {
  const fn = args[0];
  return function() { return K.callFn(fn, Array.from(arguments)); };
});
K.registerNative('host-typeof', args => typeof args[0]);
K.registerNative('host-await', args => args[0]);

K.eval('(define SX_VERSION "pre-screen-1.0")');
K.eval('(define SX_ENGINE "ocaml-vm-wasm-test")');
K.eval('(define parse sx-parse)');
K.eval('(define serialize sx-serialize)');

// Stub DOM primitives for HS runtime
K.eval('(define dom-add-class (fn (el cls) nil))');
K.eval('(define dom-remove-class (fn (el cls) nil))');
K.eval('(define dom-has-class? (fn (el cls) false))');
K.eval('(define dom-listen (fn (target event-name handler) nil))');

// Load hyperscript modules (tokenizer, parser, compiler — skip runtime for pure compilation)
const hsFiles = [
  'lib/hyperscript/tokenizer.sx',
  'lib/hyperscript/parser.sx',
  'lib/hyperscript/compiler.sx',
];
for (const f of hsFiles) {
  const src = fs.readFileSync(path.join(PROJECT_ROOT, f), 'utf8');
  const r = K.load(src);
  if (typeof r === 'string' && r.startsWith('Error')) {
    console.error(`Load failed: ${f}: ${r}`);
    process.exit(1);
  }
}

// Read batch input
const batchFile = process.env.HS_BATCH_FILE;
if (!batchFile) { console.error('No HS_BATCH_FILE env var'); process.exit(1); }
const sources = JSON.parse(fs.readFileSync(batchFile, 'utf8'));

// Test each source with a per-source timeout
for (const src of sources) {
  // Escape the source for embedding in SX string literal
  const escaped = src.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
  const expr = `(inspect (hs-to-sx-from-source "${escaped}"))`;

  let status = 'ok';
  let timedOut = false;

  // Use a synchronous approach: just try eval directly.
  // The parent process handles the overall batch timeout.
  try {
    const result = K.eval(expr);
    if (result === null || result === undefined) {
      status = 'error';
    } else if (typeof result === 'string' && result.startsWith('Error')) {
      status = 'error';
    }
  } catch (e) {
    status = 'error';
  }

  console.log(`RESULT:${JSON.stringify({ source: src, status })}`);
}
