import { isPerformanceAPIAvailable } from './is-performance-api-available';
/**
 * Monitors if a pages enters a visibility state which will lead to
 * distorted duration measurements (where the measurement uses the
 * requestAnimationFrame api).
 */
export var getDistortedDurationMonitor = function getDistortedDurationMonitor() {
  if (typeof document === 'undefined') {
    return {
      distortedDuration: false,
      cleanup: function cleanup() {}
    };
  }
  // If an editor is rendered when the document is not visible -- the callback passed to
  // requestAnimationFrame will not fire until the document becomes visible again.
  //
  // For the purposes of using performance measurement -- this behaviour means the events
  // which have been fired in the background are not useful -- and lead to other events
  // being hard to draw conclusions from.
  //
  // To mitigate this -- we detect this state -- and fire a separate callback when the
  // measurement has occurred when the render was in the background
  var distortedDuration = document.visibilityState !== 'visible';
  function handleVisibilityChange() {
    if (document.visibilityState !== 'visible') {
      distortedDuration = true;
    }
  }

  // Ignored via go/ees005
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
  document.addEventListener('visibilitychange', handleVisibilityChange);
  return {
    distortedDuration: distortedDuration,
    /**
     * Cleans up the document visibility event listener
     */
    cleanup: function cleanup() {
      // Ignored via go/ees005
      // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    }
  };
};

/**
 * Measures time it takes to render a frame including -> style, paint, layout and composition.
 *
 * How does it work:
 * 1. We mark the beginning of a render with the `startMark`
 * 2. We schedule `requestAnimationFrame` callback for the next frame
 * 3. Framework (e.g. prosemirror) does its magic and mounts dom nodes "synchronously"
 * 4. When the main thread is unblocked our callback gets executed and onMeasureComplete is being called
 *
 * Why does it work:
 * | javascript (framework)           | style | layout | paint | composite | javascript  | ...
 *  | startMark + scheduling rAF |                                                       | rAF callback, endMark
 */
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
export function measureRender(
/**
 * Unique name for the measurement
 *
 * Important: if multiple measureRender events are fired at the same time
 * with the same measure name -- the result will not be correct.
 */
measureName,
/**
 * Call back fired when the measurement completes.
 *
 * Note: when this function is called when the Document.visibilityState is not
 * visible -- the duration is likely to be misleading/inaccurate. This is due
 * to the measurements use of the `requestAnimationFrame` api which only fires
 * when the Document.visibilityState is visible.
 */
onMeasureComplete) {
  if (!isPerformanceAPIAvailable()) {
    return;
  }
  var startMark = "[START]: ".concat(measureName);
  var endMark = "[END]: ".concat(measureName);
  var startTime = performance.now();
  performance.mark(startMark);
  var distortedDurationMonitor = getDistortedDurationMonitor();
  requestAnimationFrame(function () {
    requestAnimationFrame(function () {
      performance.mark(endMark);
      distortedDurationMonitor.cleanup();
      var duration = performance.now() - startTime;
      try {
        performance.measure(measureName, startMark, endMark);
        var entry = performance.getEntriesByName(measureName).pop();
        if (!entry) {
          onMeasureComplete({
            duration: duration,
            startTime: startTime,
            distortedDuration: distortedDurationMonitor.distortedDuration
          });
        } else {
          onMeasureComplete({
            duration: entry.duration,
            startTime: entry.startTime,
            distortedDuration: distortedDurationMonitor.distortedDuration
          });
        }
      } catch (e) {
        onMeasureComplete({
          duration: duration,
          startTime: startTime,
          distortedDuration: distortedDurationMonitor.distortedDuration
        });
      }
      performance.clearMeasures(measureName);
      performance.clearMarks(startMark);
      performance.clearMarks(endMark);
    });
  });
}