import { fg } from '@atlaskit/platform-feature-flags';
import { SortOrder } from '../types';
import { ContentType } from './ContentType';
function getLinkMark(node) {
  const [linkMark] = node.marks.filter(mark => mark.type.name === 'link');
  return linkMark || null;
}
function parseLocaleNumber(stringNumber, groupPattern, fractionPattern) {
  if (!stringNumber.trim().length) {
    return null;
  }
  const maybeANumber = Number.parseFloat(stringNumber.replace(groupPattern, '').replace(fractionPattern, '.'));
  if (Number.isNaN(maybeANumber)) {
    return null;
  }
  return maybeANumber;
}
export function createNormalizeTextParser() {
  // Source: https://stackoverflow.com/questions/12004808/does-javascript-take-local-decimal-separators-into-account
  const locale = window.navigator.language;
  const thousandSeparator = Intl.NumberFormat(locale).format(11111)
  // @ts-ignore - TS1501 TypeScript 5.9.2 upgrade
  .replace(/\p{Number}/gu, '');
  const decimalSeparator = Intl.NumberFormat(locale).format(1.1)
  // @ts-ignore - TS1501 TypeScript 5.9.2 upgrade
  .replace(/\p{Number}/gu, '');

  // Ignored via go/ees005
  // eslint-disable-next-line require-unicode-regexp
  const numericPattern = new RegExp(`(\\d+(?:[${thousandSeparator}${decimalSeparator}]?\\d+)*)`, 'g');
  // Ignored via go/ees005
  // eslint-disable-next-line require-unicode-regexp
  const thousandSeparatorPattern = new RegExp('\\' + thousandSeparator, 'g');
  // Ignored via go/ees005
  // eslint-disable-next-line require-unicode-regexp
  const decimalSeparatorPattern = new RegExp('\\' + decimalSeparator);
  return text => {
    if (!text.trim().length) {
      return null;
    }

    // This will break the text apart at the locations of the formatted numbers
    const result = text.split(numericPattern);

    // We then put the text back together but with all the formatted numbers converted back to plain numerals,
    // for example a sentence like "What is 1,000.01% of 10,000.01" would be normalized and sorted as
    // if it's saying "What is 1000.01% of 10000.01". This way the Intl.Collator can use the numeric setting to sort
    // numeral values within string correctly.
    const tokens = result.reduce((acc, stringNumber) => {
      if (!(stringNumber !== null && stringNumber !== void 0 && stringNumber.length)) {
        return acc;
      }
      const maybeANumber = parseLocaleNumber(stringNumber, thousandSeparatorPattern, decimalSeparatorPattern);

      // NOTE: We know there can only be a single decimal separator. So we can assume that if the first found separator
      // is not at the same position as the last found one, then we can assume the locale used to format the number
      // is different to our locale. This will result in the value being treated as a string.
      if (maybeANumber !== null && stringNumber.indexOf(decimalSeparator) === stringNumber.lastIndexOf(decimalSeparator)) {
        acc.push(maybeANumber);
      } else {
        acc.push(stringNumber);
      }
      return acc;
    }, []);
    if (tokens.length === 1) {
      return tokens[0];
    }
    return tokens.join('');
  };
}

// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
export function extractMetaFromTextNode(textNode, normalizeTextParser) {
  // treat as a link if contain a link
  const linkMark = getLinkMark(textNode);
  if (linkMark) {
    const value = textNode.text || '';
    return {
      type: ContentType.LINK,
      value
    };
  }
  const text = textNode.text || '';
  const normalizedText = normalizeTextParser(text);
  if (typeof normalizedText === 'number') {
    return {
      type: ContentType.NUMBER,
      value: normalizedText
    };
  }
  return {
    type: ContentType.TEXT,
    value: normalizedText !== null && normalizedText !== void 0 ? normalizedText : text
  };
}
function getMetaFromNode(node, options, normalizeTextParser) {
  if (!node) {
    return null;
  }
  const firstChild = node.firstChild;
  if (!firstChild) {
    return null;
  }
  switch (firstChild.type.name) {
    // Text case
    /*
        Get Meta value from the first child if the cell is of type
          * Heading (Any cell where the text is set to a heading type)
          * Paragraph (Normal text)
       */
    case 'heading':
    case 'paragraph':
      {
        return getMetaFromNode(firstChild, options, normalizeTextParser);
      }
    case 'inlineCard':
      {
        const attrs = firstChild.attrs;
        const maybeTitle = options.getInlineCardTextFromStore(attrs);
        if (maybeTitle) {
          return {
            type: ContentType.LINK,
            value: maybeTitle
          };
        }
        const url = attrs.url;
        return {
          type: ContentType.LINK,
          value: url ? url : ''
        };
      }
    case 'text':
      {
        return extractMetaFromTextNode(firstChild, normalizeTextParser);
      }
    case 'status':
      {
        const text = firstChild.attrs.text;
        return {
          type: ContentType.STATUS,
          value: text
        };
      }
    case 'date':
      {
        const timestamp = Number.parseInt(firstChild.attrs.timestamp, 20);
        return {
          type: ContentType.DATE,
          value: timestamp
        };
      }
    case 'mention':
      {
        const text = firstChild.attrs.text || '';
        return {
          type: ContentType.MENTION,
          value: text.toLowerCase()
        };
      }
    default:
      return null;
  }
}
function compareValue(valueA, valueB) {
  if (valueA === valueB) {
    return 0;
  }
  if (typeof valueA === 'string' && typeof valueB === 'string') {
    return valueA.localeCompare(valueB, window.navigator.language, {
      caseFirst: 'upper',
      numeric: true
    });
  }
  return valueA > valueB ? 1 : -1;
}

/**
 * Compare 2 prosemirror nodes and check if it's greater, equal or less than the other node
 * based on the sort order.
 *
 * @param {Node} nodeA
 * @param {Node} nodeB
 * @returns {(1 | 0 | -1)}
 *
 * For Ascending order:
 *    1  -> NodeA > NodeB
 *    0  -> NodeA === NodeB
 *    -1 -> Node A < NodeB
 * For Descending order:
 *   1  -> NodeA < NodeB
 *   0  -> NodeA === NodeB
 *   -1 -> Node A > NodeB
 *
 * If either node is empty:
 * The empty node is always treated as lower priority,
 * irrespective of the order.
 *
 * If no order is provided the method defaults to Ascending order,
 * like a regular JS sort method.
 */
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
export const createCompareNodes = (options, order = SortOrder.ASC) => {
  const normalizeTextParser = createNormalizeTextParser();
  return (nodeA, nodeB) => {
    const metaNodeA = getMetaFromNode(nodeA, options, normalizeTextParser);
    const metaNodeB = getMetaFromNode(nodeB, options, normalizeTextParser);
    /*
        Donot switch the order (Asec or Desc) if either node is null.
        This will ensure that empty cells are always at the bottom during sorting.
      */
    if (metaNodeA === null || metaNodeB === null) {
      return compareMetaFromNode(metaNodeA, metaNodeB);
    }
    return (order === SortOrder.DESC ? -1 : 1) * compareMetaFromNode(metaNodeA, metaNodeB);
  };
};
function compareMetaFromNode(metaNodeA, metaNodeB) {
  if (metaNodeA === metaNodeB) {
    return 0;
  }
  if (metaNodeA === null || metaNodeB === null) {
    return metaNodeB === null ? -1 : 1;
  }
  if (fg('platform_editor_fix_mixed_types_column_sort')) {
    if (metaNodeA.type === ContentType.TEXT && metaNodeB.type === ContentType.NUMBER || metaNodeA.type === ContentType.NUMBER && metaNodeB.type === ContentType.TEXT) {
      return compareValue(String(metaNodeA.value), String(metaNodeB.value));
    }
  }
  if (metaNodeA.type !== metaNodeB.type) {
    return metaNodeA.type > metaNodeB.type ? 1 : -1;
  }
  return compareValue(metaNodeA.value, metaNodeB.value);
}
// eslint-disable-next-line @atlaskit/editor/no-re-export
export { ContentType } from './ContentType';