const fs = require('fs');
const src = fs.readFileSync('shared/static/scripts/sx-browser.js', 'utf8');

global.window = { addEventListener: function(){}, history: { pushState:function(){}, replaceState:function(){} }, location: { pathname:'/', search:'' } };
global.document = {
  readyState: 'complete',
  createElement: function() { return { setAttribute:function(){}, appendChild:function(){}, style:{}, addEventListener:function(){} }; },
  createDocumentFragment: function() { return { appendChild:function(){}, childNodes:[] }; },
  createTextNode: function(t) { return { textContent: t, nodeType: 3 }; },
  createElementNS: function() { return { setAttribute:function(){}, appendChild:function(){} }; },
  head: { querySelector:function(){return null;}, appendChild:function(){} },
  body: { querySelectorAll:function(){return [];}, querySelector:function(){return null;}, getAttribute:function(){return null;} },
  querySelectorAll: function(){return [];},
  querySelector: function(){return null;},
  addEventListener: function(){},
  cookie: ''
};
global.navigator = { serviceWorker: { register: function() { return { then: function(f) { return { catch: function(){} }; } }; } } };
global.localStorage = { getItem:function(){return null;}, setItem:function(){}, removeItem:function(){} };
global.CustomEvent = function(n,o){ this.type=n; this.detail=(o||{}).detail; };
global.MutationObserver = function(){ return { observe:function(){} }; };
global.HTMLElement = function(){};
global.EventSource = function(){};

// Prevent module.exports detection so it sets global.Sx
var _module = module;
module = undefined;
var patchedSrc = fs.readFileSync('/tmp/sx-browser-patched.js', 'utf8');
eval(patchedSrc);
module = _module;
var Sx = global.Sx;
console.log('Sx loaded:', Sx ? true : false);

var env = Object.create(Sx.componentEnv);

// Test 1: computed with SX lambda
try {
  var r = Sx.eval(Sx.parse('(let ((a (signal 3)) (b (computed (fn () (* 2 (deref a)))))) (deref b))')[0], env);
  console.log('TEST 1 computed:', r, '(expected 6)', r === 6 ? 'PASS' : 'FAIL');
} catch(e) {
  console.log('TEST 1 computed ERROR:', e.message);
}

// Test 2: swap! with dec
try {
  var r2 = Sx.eval(Sx.parse('(let ((s (signal 10))) (swap! s dec) (deref s))')[0], env);
  console.log('TEST 2 swap!:', r2, '(expected 9)', r2 === 9 ? 'PASS' : 'FAIL');
} catch(e) {
  console.log('TEST 2 swap! ERROR:', e.message);
}

// Test 3: effect with SX lambda
try {
  var r3 = Sx.eval(Sx.parse('(let ((s (signal 0)) (log (list)) (_e (effect (fn () (append! log (deref s)))))) (reset! s 1) (first log))')[0], env);
  console.log('TEST 3 effect:', r3, '(expected 0)', r3 === 0 ? 'PASS' : 'FAIL');
} catch(e) {
  console.log('TEST 3 effect ERROR:', e.message);
}

// Test 4: effect re-runs on change
try {
  var r4 = Sx.eval(Sx.parse('(let ((s (signal 0)) (log (list)) (_e (effect (fn () (append! log (deref s)))))) (reset! s 5) (last log))')[0], env);
  console.log('TEST 4 effect re-run:', r4, '(expected 5)', r4 === 5 ? 'PASS' : 'FAIL');
} catch(e) {
  console.log('TEST 4 effect re-run ERROR:', e.message);
}

// Test 5: on-click handler lambda
try {
  var r5 = Sx.eval(Sx.parse('(let ((s (signal 0)) (f (fn (e) (swap! s inc)))) (f nil) (f nil) (deref s))')[0], env);
  console.log('TEST 5 lambda call:', r5, '(expected 2)', r5 === 2 ? 'PASS' : 'FAIL');
} catch(e) {
  console.log('TEST 5 lambda call ERROR:', e.message);
}

// Test 6: defisland + renderToDom simulation
try {
  // Override DOM stubs with tracking versions
  var listenCalls = [];
  // Override methods on the EXISTING document object (don't replace)
  document.createElement = function(tag) {
    var el = {
      tagName: tag,
      childNodes: [],
      style: {},
      setAttribute: function(k, v) { this['_attr_'+k] = v; },
      getAttribute: function(k) { return this['_attr_'+k]; },
      appendChild: function(c) { this.childNodes.push(c); return c; },
      addEventListener: function(name, fn) { listenCalls.push({el: this, name: name, fn: fn}); },
      removeEventListener: function() {},
      textContent: '',
      nodeType: 1
    };
    return el;
  };
  document.createTextNode = function(t) { return { textContent: String(t), nodeType: 3 }; };
  document.createDocumentFragment = function() {
    return {
      childNodes: [],
      appendChild: function(c) { if (c) this.childNodes.push(c); return c; },
      nodeType: 11
    };
  };

  var env2 = Object.create(Sx.componentEnv);

  // Define island
  Sx.eval(Sx.parse('(defisland ~test-click (&key initial) (let ((count (signal (or initial 0)))) (div (button :on-click (fn (e) (swap! count inc)) "Click") (span (deref count)))))')[0], env2);

  // Patch domListen to trace calls
  // The domListen is inside the IIFE closure, so we can't patch it directly.
  // Instead, patch addEventListener on elements by wrapping createElement
  var origCE = document.createElement;
  document.createElement = function(tag) {
    var el = origCE(tag);
    var origAEL = el.addEventListener;
    el.addEventListener = function(name, fn) {
      console.log('  addEventListener called:', tag, name);
      listenCalls.push({el: el, name: name, fn: fn});
      origAEL.call(el, name, fn);
    };
    return el;
  };

  // Temporarily hook into isCallable and domListen for debugging
  // We can't patch the closure vars directly, but we can test via eval
  var testLambda = Sx.eval(Sx.parse('(fn (e) e)')[0], env2);
  console.log('  lambda type:', typeof testLambda, testLambda ? testLambda._lambda : 'no _lambda');
  console.log('  Sx.isTruthy(lambda):', Sx.isTruthy(testLambda));

  // Test what render-dom-element does with on-click
  // Simpler test: just a button with on-click, no island
  var parsed = Sx.parse('(button :on-click (fn (e) nil) "test")')[0];
  console.log('  parsed expr:', JSON.stringify(parsed, function(k,v) {
    if (v && v._sym) return 'SYM:' + v.name;
    if (v && v._kw) return 'KW:' + v.name;
    return v;
  }));
  var simpleTest = Sx.renderToDom(parsed, env2, null);
  console.log('  simple button rendered:', simpleTest ? simpleTest.tagName : 'null');
  console.log('  listeners after simple:', listenCalls.length);

  // Render it
  var dom = Sx.renderToDom(Sx.parse('(~test-click :initial 0)')[0], env2, null);
  console.log('TEST 6 island rendered:', dom ? 'yes' : 'no');
  console.log('  listeners attached:', listenCalls.length);

  if (listenCalls.length > 0) {
    var clickHandler = listenCalls[0];
    console.log('  event name:', clickHandler.name);
    // Simulate click
    clickHandler.fn({type: 'click'});
    // Find the span's text node
    var container = dom; // div[data-sx-island]
    var innerDiv = container.childNodes[0]; // the body div
    console.log('  container tag:', container.tagName);
    console.log('  container children:', container.childNodes.length);
    if (innerDiv && innerDiv.childNodes) {
      console.log('  innerDiv tag:', innerDiv.tagName);
      console.log('  innerDiv children:', innerDiv.childNodes.length);
      var button = innerDiv.childNodes[0];
      var span = innerDiv.childNodes[1];
      console.log('  button tag:', button ? button.tagName : 'none');
      console.log('  span tag:', span ? span.tagName : 'none');
      if (span && span.childNodes && span.childNodes[0]) {
        console.log('  span text BEFORE click effect:', span.childNodes[0].textContent);
      }
    }
    // Click again
    clickHandler.fn({type: 'click'});
    if (innerDiv && innerDiv.childNodes) {
      var span2 = innerDiv.childNodes[1];
      if (span2 && span2.childNodes && span2.childNodes[0]) {
        console.log('  span text AFTER 2 clicks:', span2.childNodes[0].textContent);
        console.log('  TEST 6:', span2.childNodes[0].textContent === '2' ? 'PASS' : 'FAIL (expected 2, got ' + span2.childNodes[0].textContent + ')');
      }
    }
  } else {
    console.log('  TEST 6: FAIL (no listeners attached)');
  }
} catch(e) {
  console.log('TEST 6 island ERROR:', e.message);
  console.log(e.stack.split('\n').slice(0,5).join('\n'));
}
