/**
 * @license React
 * scheduler-unstable_post_task.production.min.js
 *
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

// TODO: Use symbols?
const ImmediatePriority = 1;
const UserBlockingPriority = 2;
const NormalPriority = 3;
const LowPriority = 4;
const IdlePriority = 5;

const perf = window.performance;
const setTimeout = window.setTimeout; // Use experimental Chrome Scheduler postTask API.

const scheduler = global.scheduler;
const getCurrentTime = perf.now.bind(perf);
const unstable_now = getCurrentTime; // Scheduler periodically yields in case there is other work on the main
// thread, like user events. By default, it yields multiple times per frame.
// It does not attempt to align with frame boundaries, since most tasks don't
// need to be frame aligned; for those that do, use requestAnimationFrame.

const yieldInterval = 5;
let deadline = 0;
let currentPriorityLevel_DEPRECATED = NormalPriority; // `isInputPending` is not available. Since we have no way of knowing if
// there's pending input, always yield at the end of the frame.

function unstable_shouldYield() {
  return getCurrentTime() >= deadline;
}
function unstable_requestPaint() {// Since we yield every frame regardless, `requestPaint` has no effect.
}
function unstable_scheduleCallback(priorityLevel, callback, options) {
  let postTaskPriority;

  switch (priorityLevel) {
    case ImmediatePriority:
    case UserBlockingPriority:
      postTaskPriority = 'user-blocking';
      break;

    case LowPriority:
    case NormalPriority:
      postTaskPriority = 'user-visible';
      break;

    case IdlePriority:
      postTaskPriority = 'background';
      break;

    default:
      postTaskPriority = 'user-visible';
      break;
  }

  const controller = new TaskController({
    priority: postTaskPriority
  });
  const postTaskOptions = {
    delay: typeof options === 'object' && options !== null ? options.delay : 0,
    signal: controller.signal
  };
  const node = {
    _controller: controller
  };
  scheduler.postTask(runTask.bind(null, priorityLevel, postTaskPriority, node, callback), postTaskOptions).catch(handleAbortError);
  return node;
}

function runTask(priorityLevel, postTaskPriority, node, callback) {
  deadline = getCurrentTime() + yieldInterval;

  try {
    currentPriorityLevel_DEPRECATED = priorityLevel;
    const didTimeout_DEPRECATED = false;
    const result = callback(didTimeout_DEPRECATED);

    if (typeof result === 'function') {
      // Assume this is a continuation
      const continuation = result;
      const continuationOptions = {
        signal: node._controller.signal
      };
      const nextTask = runTask.bind(null, priorityLevel, postTaskPriority, node, continuation);

      if (scheduler.yield !== undefined) {
        scheduler.yield(continuationOptions).then(nextTask).catch(handleAbortError);
      } else {
        scheduler.postTask(nextTask, continuationOptions).catch(handleAbortError);
      }
    }
  } catch (error) {
    // We're inside a `postTask` promise. If we don't handle this error, then it
    // will trigger an "Unhandled promise rejection" error. We don't want that,
    // but we do want the default error reporting behavior that normal
    // (non-Promise) tasks get for unhandled errors.
    //
    // So we'll re-throw the error inside a regular browser task.
    setTimeout(() => {
      throw error;
    });
  } finally {
    currentPriorityLevel_DEPRECATED = NormalPriority;
  }
}

function handleAbortError(error) {// Abort errors are an implementation detail. We don't expose the
  // TaskController to the user, nor do we expose the promise that is returned
  // from `postTask`. So we should suppress them, since there's no way for the
  // user to handle them.
}

function unstable_cancelCallback(node) {
  const controller = node._controller;
  controller.abort();
}
function unstable_runWithPriority(priorityLevel, callback) {
  const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
  currentPriorityLevel_DEPRECATED = priorityLevel;

  try {
    return callback();
  } finally {
    currentPriorityLevel_DEPRECATED = previousPriorityLevel;
  }
}
function unstable_getCurrentPriorityLevel() {
  return currentPriorityLevel_DEPRECATED;
}
function unstable_next(callback) {
  let priorityLevel;

  switch (currentPriorityLevel_DEPRECATED) {
    case ImmediatePriority:
    case UserBlockingPriority:
    case NormalPriority:
      // Shift down to normal priority
      priorityLevel = NormalPriority;
      break;

    default:
      // Anything lower than normal priority should remain at the current level.
      priorityLevel = currentPriorityLevel_DEPRECATED;
      break;
  }

  const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
  currentPriorityLevel_DEPRECATED = priorityLevel;

  try {
    return callback();
  } finally {
    currentPriorityLevel_DEPRECATED = previousPriorityLevel;
  }
}
function unstable_wrapCallback(callback) {
  const parentPriorityLevel = currentPriorityLevel_DEPRECATED;
  return () => {
    const previousPriorityLevel = currentPriorityLevel_DEPRECATED;
    currentPriorityLevel_DEPRECATED = parentPriorityLevel;

    try {
      return callback();
    } finally {
      currentPriorityLevel_DEPRECATED = previousPriorityLevel;
    }
  };
}
function unstable_forceFrameRate() {}
function unstable_pauseExecution() {}
function unstable_continueExecution() {}
function unstable_getFirstCallbackNode() {
  return null;
} // Currently no profiling build

const unstable_Profiling = null;

exports.unstable_IdlePriority = IdlePriority;
exports.unstable_ImmediatePriority = ImmediatePriority;
exports.unstable_LowPriority = LowPriority;
exports.unstable_NormalPriority = NormalPriority;
exports.unstable_Profiling = unstable_Profiling;
exports.unstable_UserBlockingPriority = UserBlockingPriority;
exports.unstable_cancelCallback = unstable_cancelCallback;
exports.unstable_continueExecution = unstable_continueExecution;
exports.unstable_forceFrameRate = unstable_forceFrameRate;
exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel;
exports.unstable_getFirstCallbackNode = unstable_getFirstCallbackNode;
exports.unstable_next = unstable_next;
exports.unstable_now = unstable_now;
exports.unstable_pauseExecution = unstable_pauseExecution;
exports.unstable_requestPaint = unstable_requestPaint;
exports.unstable_runWithPriority = unstable_runWithPriority;
exports.unstable_scheduleCallback = unstable_scheduleCallback;
exports.unstable_shouldYield = unstable_shouldYield;
exports.unstable_wrapCallback = unstable_wrapCallback;