// From ths API Filter Starts

function deepEqual(obj1, obj2) {
  if (obj1 === obj2) return true;

  if (typeof obj1 === 'string' && typeof obj2 === 'string') {
    return normalizeText(stripHTML4(obj1)) === normalizeText(stripHTML4(obj2));
  }

  if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) {
    return false;
  }

  const keys1 = Object.keys(obj1).sort();
  const keys2 = Object.keys(obj2).sort();
  if (keys1.length !== keys2.length) return false;

  for (const key of keys1) {
    if (!keys2.includes(key)) return false;

    let val1 = obj1[key];
    let val2 = obj2[key];

    if (typeof val1 === 'string' && typeof val2 === 'string') {
      val1 = normalizeText(stripHTML4(val1));
      val2 = normalizeText(stripHTML4(val2));
    }

    if (Array.isArray(val1) && Array.isArray(val2)) {
      if (val1.length !== val2.length) return false;

      const usedIndices = new Set();
      for (const item1 of val1) {
        const matchIndex = val2.findIndex((item2, idx) => {
          if (usedIndices.has(idx)) return false;
          if (deepEqual(item1, item2)) {
            usedIndices.add(idx);
            return true;
          }
          return false;
        });
        if (matchIndex === -1) return false;
      }
      continue;
    }

    if (typeof val1 === 'object' && typeof val2 === 'object') {
      if (!deepEqual(val1, val2)) return false;
    } else if (val1 !== val2) {
      return false;
    }
  }

  return true;
}

function normalizeText(text) {
  if (typeof text !== 'string') return '';

  return text
    .replace(/&nbsp;/gi, '')        // Remove non-breaking spaces
    .replace(/\u200B|\uFEFF/g, '')  // Remove hidden Unicode characters
    .replace(/\./g, '')             // Remove full stops only
    .replace(/\s+/g, '')            // Remove all types of spaces
    .toLowerCase();                 // Normalize case
}

function stripHTML4(htmlString) {
  if (typeof htmlString !== 'string') {
    htmlString = String(htmlString || '');
  }

  const div = document.createElement('div');
  div.innerHTML = htmlString.replace(/&nbsp;/gi, ' '); // convert first

  function walkAndClean(node) {
    let result = '';
    for (const child of node.childNodes) {
      if (child.nodeType === Node.TEXT_NODE) {
        result += ' ' + child.nodeValue;
      } else if (child.nodeType === Node.ELEMENT_NODE) {
        result += ' ' + walkAndClean(child);
      }
    }
    return result;
  }

  return walkAndClean(div)
    .replace(/\u200B|\uFEFF/g, '')     // remove hidden Unicode characters
    .replace(/[.]/g, '')               // remove full stops
    .replace(/\s+/g, ' ')              // normalize whitespace
    .trim()
    .toLowerCase();
}

function normalizeConditionsForGrouping(conditions) {
  if (!Array.isArray(conditions)) return [];

  return conditions.map(set => {
    const normalizedProps = (set.properties || []).map(prop => {
      const copy = { ...prop };
      delete copy.case_sensitive;

      for (const key in copy) {
        if (typeof copy[key] === 'string') {
          copy[key] = normalizeText(stripHTML4(copy[key]));
        } else if (Array.isArray(copy[key])) {
          copy[key] = copy[key].map(v =>
            typeof v === 'string' ? normalizeText(stripHTML4(v)) : v
          ).sort();
        }
      }

      return sortObjectValues(copy);
    }).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));

    return {
      match_type: set.match_type?.toLowerCase(),
      properties: normalizedProps
    };
  });
}

function areConditionsEquivalent(setA, setB) {
  if (setA.length !== setB.length) return false;

  const permutations = generatePermutations(setB);
  return permutations.some(perm => {

    return setA.every((a, i) => deepEqual(a, perm[i]));
  });
}

function generatePermutations(array) {
  if (array.length <= 1) return [array];

  const perms = [];
  for (let i = 0; i < array.length; i++) {
    const rest = [...array.slice(0, i), ...array.slice(i + 1)];
    for (const perm of generatePermutations(rest)) {
      perms.push([array[i], ...perm]);
    }
  }
  return perms;
}

function findDuplicatesWithinType(records, duplicatesArray) {
  const seenRecords = [];

  records.forEach((rule) => {
    const { summary, actions, conditions } = rule;

    if (summary && actions && conditions) {
      const normalizedSummary = sortObjectValues(summary);
      const normalizedActions = sortObjectValues(actions);
      const normalizedConditions = normalizeConditionsForGrouping(conditions);

      const duplicate = seenRecords.find(seen => {
        const summaryMatch = deepEqual(sortObjectValues(seen.summary), normalizedSummary);
        const actionMatch = deepEqual(sortObjectValues(seen.actions), normalizedActions);
        const conditionMatch = areConditionsEquivalent(seen.conditions, normalizedConditions);

        return summaryMatch && actionMatch && conditionMatch;
      });

      if (duplicate) {
        duplicatesArray.push(rule);
        if (!duplicatesArray.includes(duplicate.rule)) {
          duplicatesArray.push(duplicate.rule);
        }
      } else {
        seenRecords.push({
          summary: normalizedSummary,
          actions: normalizedActions,
          conditions: normalizedConditions,
          rule
        });
      }
    }
  });
}

function sortObjectValues(obj) {
  if (Array.isArray(obj)) {
    return obj
      .map(item =>
        typeof item === 'object' && item !== null
          ? sortObjectValues(item)
          : normalizeText(stripHTML4(item))
      )
      .sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
  }

  if (typeof obj === 'object' && obj !== null) {
  if (Object.prototype.hasOwnProperty.call(obj, 'condition_set_1') || 
    Object.prototype.hasOwnProperty.call(obj, 'condition_set_2')) {

      const { operator, ...rest } = obj;
      const allConditions = Object.values(rest).flat().map(val =>
        normalizeText(stripHTML4(val))
      ).sort();

      return {
        operator: normalizeText(stripHTML4(operator || '')),
        conditions: allConditions
      };
    }

    return Object.fromEntries(
      Object.entries(obj)
        .map(([key, value]) => [key, sortObjectValues(value)])
        .sort(([a], [b]) => a.localeCompare(b))
    );
  }

  return normalizeText(stripHTML4(obj));
}


in this code, 

any function is missing from findduplicateswithtype