import * as Context from "@effect/data/Context";
import { NodeInspectSymbol, toString } from "@effect/data/Inspectable";
import * as Option from "@effect/data/Option";
import { pipeArguments } from "@effect/data/Pipeable";
import * as Predicate from "@effect/data/Predicate";
import * as Exit from "@effect/io/Exit";
import * as Fiber from "@effect/io/Fiber";
import * as FiberId from "@effect/io/FiberId";
import * as FiberRefs from "@effect/io/FiberRefs";
import { NoSuchElementException } from "@effect/io/internal/cause";
import * as InternalCause from "@effect/io/internal/cause";
import * as core from "@effect/io/internal/core";
import * as FiberRuntime from "@effect/io/internal/fiberRuntime";
import * as fiberScope from "@effect/io/internal/fiberScope";
import * as OpCodes from "@effect/io/internal/opCodes/effect";
import * as runtimeFlags from "@effect/io/internal/runtimeFlags";
import * as _supervisor from "@effect/io/internal/supervisor";
import * as _scheduler from "@effect/io/Scheduler";
/** @internal */
export const unsafeFork = runtime => (self, options) => {
  const fiberId = FiberId.unsafeMake();
  const effect = self;
  let fiberRefs = FiberRefs.updatedAs(runtime.fiberRefs, {
    fiberId,
    fiberRef: core.currentContext,
    value: runtime.context
  });
  if (options?.scheduler) {
    fiberRefs = FiberRefs.updatedAs(fiberRefs, {
      fiberId,
      fiberRef: _scheduler.currentScheduler,
      value: options.scheduler
    });
  }
  if (options?.updateRefs) {
    fiberRefs = options.updateRefs(fiberRefs, fiberId);
  }
  const fiberRuntime = new FiberRuntime.FiberRuntime(fiberId, FiberRefs.forkAs(fiberRefs, fiberId), runtime.runtimeFlags);
  const supervisor = fiberRuntime._supervisor;
  if (supervisor !== _supervisor.none) {
    supervisor.onStart(runtime.context, effect, Option.none(), fiberRuntime);
    fiberRuntime.addObserver(exit => supervisor.onEnd(exit, fiberRuntime));
  }
  fiberScope.globalScope.add(runtime.runtimeFlags, fiberRuntime);
  fiberRuntime.start(effect);
  return fiberRuntime;
};
/** @internal */
export const unsafeRunCallback = runtime => (effect, onExit) => {
  const fiberRuntime = unsafeFork(runtime)(effect);
  if (onExit) {
    fiberRuntime.addObserver(exit => {
      onExit(exit);
    });
  }
  return (id, onExitInterrupt) => unsafeRunCallback(runtime)(Fiber.interruptAs(id ?? FiberId.none)(fiberRuntime), onExitInterrupt ? exit => onExitInterrupt(Exit.flatten(exit)) : void 0);
};
/** @internal */
export const unsafeRunSync = runtime => effect => {
  const result = unsafeRunSyncExit(runtime)(effect);
  if (result._tag === "Failure") {
    throw fiberFailure(result.i0);
  } else {
    return result.i0;
  }
};
/** @internal */
const asyncFiberException = fiber => {
  const limit = Error.stackTraceLimit;
  Error.stackTraceLimit = 0;
  const error = new Error();
  Error.stackTraceLimit = limit;
  const message = `Fiber #${fiber.id().id} cannot be be resolved synchronously, this is caused by using runSync on an effect that performs async work`;
  const _tag = "AsyncFiberException";
  Object.defineProperties(error, {
    _tag: {
      value: _tag
    },
    message: {
      value: message
    },
    name: {
      value: _tag
    },
    toString: {
      get() {
        return () => message;
      }
    },
    [NodeInspectSymbol]: {
      get() {
        return () => message;
      }
    }
  });
  return error;
};
/** @internal */
export const isAsyncFiberException = u => Predicate.isTagged(u, "AsyncFiberException") && "fiber" in u;
/** @internal */
export const FiberFailureId = /*#__PURE__*/Symbol.for("@effect/io/Runtime/FiberFailure");
/** @internal */
export const FiberFailureCauseId = /*#__PURE__*/Symbol.for("@effect/io/Runtime/FiberFailure/Cause");
/** @internal */
export const fiberFailure = cause => {
  const limit = Error.stackTraceLimit;
  Error.stackTraceLimit = 0;
  const error = new Error();
  Error.stackTraceLimit = limit;
  const prettyErrors = InternalCause.prettyErrors(cause);
  if (prettyErrors.length > 0) {
    const head = prettyErrors[0];
    error.name = head.message.split(":")[0];
    error.message = head.message.substring(error.name.length + 2);
    error.stack = `${error.name}: ${error.message}\n${head.stack}`;
  }
  error[FiberFailureId] = FiberFailureId;
  error[FiberFailureCauseId] = cause;
  error.toJSON = () => {
    return {
      _id: "FiberFailure",
      cause: cause.toJSON()
    };
  };
  error.toString = () => {
    return toString(error.toJSON());
  };
  error[NodeInspectSymbol] = () => {
    return error.toJSON();
  };
  return error;
};
/** @internal */
export const isFiberFailure = u => typeof u === "object" && u !== null && FiberFailureId in u;
const fastPath = effect => {
  const op = effect;
  switch (op._tag) {
    case "Failure":
    case "Success":
      {
        // @ts-expect-error
        return op;
      }
    case "Left":
      {
        return core.exitFail(op.left);
      }
    case "Right":
      {
        return core.exitSucceed(op.right);
      }
    case "Some":
      {
        return core.exitSucceed(op.value);
      }
    case "None":
      {
        // @ts-expect-error
        return core.exitFail(NoSuchElementException());
      }
  }
};
/** @internal */
export const unsafeRunSyncExit = runtime => effect => {
  const op = fastPath(effect);
  if (op) {
    return op;
  }
  const scheduler = new _scheduler.SyncScheduler();
  const fiberRuntime = unsafeFork(runtime)(effect, {
    scheduler
  });
  scheduler.flush();
  const result = fiberRuntime.unsafePoll();
  if (result) {
    return result;
  }
  throw asyncFiberException(fiberRuntime);
};
/** @internal */
export const unsafeRunPromise = runtime => effect => unsafeRunPromiseExit(runtime)(effect).then(result => {
  switch (result._tag) {
    case OpCodes.OP_SUCCESS:
      {
        return result.i0;
      }
    case OpCodes.OP_FAILURE:
      {
        throw fiberFailure(result.i0);
      }
  }
});
/** @internal */
export const unsafeRunPromiseExit = runtime => effect => new Promise(resolve => {
  const op = fastPath(effect);
  if (op) {
    resolve(op);
  }
  unsafeFork(runtime)(effect).addObserver(exit => {
    resolve(exit);
  });
});
/** @internal */
export class RuntimeImpl {
  constructor(context, runtimeFlags, fiberRefs) {
    this.context = context;
    this.runtimeFlags = runtimeFlags;
    this.fiberRefs = fiberRefs;
  }
  pipe() {
    return pipeArguments(this, arguments);
  }
}
/** @internal */
export const make = options => new RuntimeImpl(options.context, options.runtimeFlags, options.fiberRefs);
/** @internal */
export const runtime = () => core.withFiberRuntime((state, status) => core.succeed(new RuntimeImpl(state.getFiberRef(core.currentContext), status.runtimeFlags, state.getFiberRefs())));
/** @internal */
export const defaultRuntimeFlags = /*#__PURE__*/runtimeFlags.make(runtimeFlags.Interruption, runtimeFlags.CooperativeYielding, runtimeFlags.RuntimeMetrics);
/** @internal */
export const defaultRuntime = /*#__PURE__*/make({
  context: /*#__PURE__*/Context.empty(),
  runtimeFlags: defaultRuntimeFlags,
  fiberRefs: /*#__PURE__*/FiberRefs.unsafeMake( /*#__PURE__*/new Map())
});
/** @internal */
export const unsafeRunEffect = /*#__PURE__*/unsafeRunCallback(defaultRuntime);
/** @internal */
export const unsafeForkEffect = /*#__PURE__*/unsafeFork(defaultRuntime);
/** @internal */
export const unsafeRunPromiseEffect = /*#__PURE__*/unsafeRunPromise(defaultRuntime);
/** @internal */
export const unsafeRunPromiseExitEffect = /*#__PURE__*/unsafeRunPromiseExit(defaultRuntime);
/** @internal */
export const unsafeRunSyncEffect = /*#__PURE__*/unsafeRunSync(defaultRuntime);
/** @internal */
export const unsafeRunSyncExitEffect = /*#__PURE__*/unsafeRunSyncExit(defaultRuntime);
// circular with Effect
/** @internal */
export const asyncEffect = register => core.flatMap(core.deferredMake(), deferred => core.flatMap(runtime(), runtime => core.uninterruptibleMask(restore => core.zipRight(FiberRuntime.fork(restore(core.catchAllCause(register(cb => unsafeRunCallback(runtime)(core.intoDeferred(cb, deferred))), cause => core.deferredFailCause(deferred, cause)))), restore(core.deferredAwait(deferred))))));
//# sourceMappingURL=runtime.mjs.map