#!/usr/bin/env node
/**
 * mutate-operators (Level B) — чистый генератор валидных операторных мутантов JS-исходника.
 * Текстовая замена операторов (без AST-зависимости): каждый оператор флипается на свою пару
 * по каждому вхождению, по одному мутанту за вхождение. Цель — НЕ исчерпать пространство, а
 * получить горсть валидных мутантов, чтобы тест критерия имел шанс кого-то «убить». Невалидные/
 * эквивалентные мутанты (задели строку/коммент) отсеивает baseline+run в mutate-runner (выживший/
 * load-error). Детерминирован (стабильный порядок), dedup, cap (ограничение времени гейта, SE-LB-13).
 * AST-точность — опциональное усиление (судья М4 / отдельный план).
 *
 * Операторы выбраны без capture-групп → замена n-го вхождения тривиальна и корректна.
 * Пары не перекрываются: /===/ не матчит '!==' (две равны), /!==/ не матчит '===' (три равны),
 * />=/ не матчит '=>' (стрелка = '=' затем '>'; '>=' = '>' затем '=').
 */

// [искать (global regex), заменить (литерал, без $-групп)].
const OPS = [
  [/===/g, '!=='],
  [/!==/g, '==='],
  [/&&/g, '||'],
  [/\|\|/g, '&&'],
  [/\btrue\b/g, 'false'],
  [/\bfalse\b/g, 'true'],
  [/>=/g, '>'],
  [/<=/g, '<'],
];

export const MUTATION_OPERATORS = { cap: 40 };

/** Заменить ТОЛЬКО n-е вхождение global-regex на литерал; остальные оставить. */
function replaceNth(source, re, replacement, nth) {
  let i = -1;
  return source.replace(re, (m) => {
    i += 1;
    return i === nth ? replacement : m;
  });
}

export function generateMutants(source = '') {
  const out = [];
  const seen = new Set([source]);
  for (const [re, rep] of OPS) {
    const count = (source.match(re) || []).length;
    for (let n = 0; n < count; n += 1) {
      const mutated = replaceNth(source, new RegExp(re.source, re.flags), rep, n);
      if (mutated === source || seen.has(mutated)) continue;
      seen.add(mutated);
      out.push({ label: `${re.source}->${rep}#${n}`, mutated });
      if (out.length >= MUTATION_OPERATORS.cap) return out;
    }
  }
  return out;
}
