const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");

const app = fs.readFileSync(path.join(
  __dirname,
  "../../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/app.js"), "utf8");

function section(start, end) {
  const startIndex = app.indexOf(start);
  const endIndex = app.indexOf(end, startIndex);
  assert.ok(startIndex >= 0 && endIndex > startIndex,
    `Missing or out-of-order source markers: ${start} -> ${end}`);
  return app.slice(startIndex, endIndex);
}

function timingHarness() {
  let now = 0;
  let nextTimer = 1;
  const pending = new Map();
  const windowObject = {
    setTimeout(callback, milliseconds) {
      const timer = nextTimer++;
      pending.set(timer, { callback, due: now + milliseconds });
      return timer;
    },
    clearTimeout(timer) {
      pending.delete(timer);
    }
  };
  const source = section(
    "const fallbackDoubleClickTimeMilliseconds",
    "function isRovingListNavigationKey");
  const api = new Function("window", `
    ${source}
    return {
      update: updateDoubleClickTime,
      defer: deferLegacySingleClick,
      clear: clearDelayedClick
    };
  `)(windowObject);

  return {
    ...api,
    pendingCount: () => pending.size,
    advance(milliseconds) {
      now += milliseconds;
      const due = Array.from(pending.entries())
        .filter(([, timer]) => timer.due <= now)
        .sort((left, right) => left[1].due - right[1].due);
      due.forEach(([id, timer]) => {
        if (!pending.delete(id)) return;
        timer.callback();
      });
    }
  };
}

test("a 350ms Windows double-click cancels the pending single action", () => {
  const harness = timingHarness();
  const actions = [];
  harness.update({
    interactionMetrics: { doubleClickTimeMilliseconds: 500 }
  });

  const timer = harness.defer(() => actions.push("single"));
  harness.advance(350);
  assert.deepEqual(actions, [], "the first click must not commit before click two");
  assert.equal(harness.pendingCount(), 1);

  harness.clear(timer);
  actions.push("double");
  harness.advance(1000);
  assert.deepEqual(actions, ["double"]);
  assert.equal(harness.pendingCount(), 0);
});

test("ordinary single-click follows the live Windows interval plus a race margin", () => {
  const harness = timingHarness();
  let count = 0;
  harness.update({
    interactionMetrics: { doubleClickTimeMilliseconds: 800 }
  });
  harness.defer(() => { count += 1; });

  harness.advance(849);
  assert.equal(count, 0);
  harness.advance(1);
  assert.equal(count, 1);

  harness.update({ interactionMetrics: { doubleClickTimeMilliseconds: 5001 } });
  harness.defer(() => { count += 1; });
  harness.advance(549);
  assert.equal(count, 1, "invalid native data must use the 500ms fallback");
  harness.advance(1);
  assert.equal(count, 2);
});

test("rerender-prone rows share system timing while GraphE candidates remain double-click only", () => {
  assert.doesNotMatch(app, /manualStockResultClickTimer|queueManualFinancialStockSelection/u);
  const manualCandidateClick = section(
    "const stock = event.target.closest(\"button[data-manual-stock-result-id]\")",
    "if (event.target.closest(\"button[data-manual-save]\"))");
  assert.match(manualCandidateClick, /focusManualFinancialStock\(stock\)/u);
  assert.match(manualCandidateClick, /selectManualFinancialStock\(stock\)/u);
  assert.match(app,
    /manualViResultClickTimer = deferLegacySingleClick\(function \(\) \{/u);
  assert.match(app,
    /manualResultClickTimer = deferLegacySingleClick\(function \(\) \{/u);
  assert.equal(
    (app.match(/comparisonTargetClickTimer = deferLegacySingleClick\(function \(\) \{/gu) || [])
      .length,
    2);
  assert.match(app,
    /themeResultClickTimer = deferLegacySingleClick\(function \(\) \{/u);
  assert.doesNotMatch(app, /\}, 250\);/u);

  const operatorCatalog = section(
    "operatorCatalogStockResults.addEventListener(\"dblclick\"",
    "operatorCatalogAddStock.addEventListener(\"click\"");
  assert.match(operatorCatalog, /if \(\(event\.detail \|\| 1\) > 1\) return;/u);
  assert.doesNotMatch(operatorCatalog, /deferLegacySingleClick|setTimeout/u);

  const namedPlaylist = section(
    "namedPlaylistDefinitions.addEventListener(\"dblclick\"",
    "namedPlaylistNewTitle.addEventListener(\"input\"");
  assert.match(namedPlaylist, /namedPlaylistSelection\.isPending\(\)/u);
  assert.match(namedPlaylist, /namedPlaylistSelection\.defer\(definitionId/u);
  assert.doesNotMatch(namedPlaylist, /setTimeout|250/u);
});

test("each native state refreshes timing before rerendering clickable catalogs", () => {
  const render = section("function render(state)", "Object.keys(operatorFolderControls)");
  const timing = render.indexOf("updateDoubleClickTime(state);");
  const firstCatalog = render.indexOf("renderFixedCatalog(state);");
  const manual = render.indexOf("renderManualFinancial(state);");
  assert.ok(timing >= 0 && firstCatalog > timing && manual > timing);
});
