/**
 * Represents the result of an Effect computation as a plain value.
 *
 * An `Exit<A, E>` is either a success with an `A` or a failure with a
 * `Cause<E>`. The failure cause preserves typed errors, defects, and
 * interruptions after a workflow has finished. Use this module when completed
 * Effect results need to be inspected, transformed, filtered, or matched
 * synchronously as data.
 *
 * @since 2.0.0
 */
import type * as Cause from "./Cause.ts"
import type * as Effect from "./Effect.ts"
import * as core from "./internal/core.ts"
import * as effect from "./internal/effect.ts"
import type { Option } from "./Option.ts"
import type * as Result from "./Result.ts"
import type { NoInfer } from "./Types.ts"

const TypeId = core.ExitTypeId

/**
 * Represents the result of an Effect computation.
 *
 * **When to use**
 *
 * Use when you need to synchronously inspect whether an Effect computation
 * succeeded or failed.
 *
 * **Details**
 *
 * An `Exit<A, E>` is either `Success<A, E>` containing a value of type `A`, or
 * `Failure<A, E>` containing a `Cause<E>` describing why the computation
 * failed.
 *
 * Since `Exit` is also an `Effect`, you can yield it inside `Effect.gen`.
 *
 * **Example** (Pattern matching on an Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const success: Exit.Exit<number> = Exit.succeed(42)
 * const failure: Exit.Exit<number, string> = Exit.fail("error")
 *
 * const result = Exit.match(success, {
 *   onSuccess: (value) => `Got value: ${value}`,
 *   onFailure: (cause) => `Got error: ${cause}`
 * })
 * ```
 *
 * @see {@link Success} for the success case
 * @see {@link Failure} for the failure case
 * @see {@link match} for pattern matching
 *
 * @category models
 * @since 2.0.0
 */
export type Exit<A, E = never> = Success<A, E> | Failure<A, E>

/**
 * Namespace containing helper types shared by `Exit` values.
 *
 * **When to use**
 *
 * Use to reference helper types that describe the shared structure of `Exit`
 * values.
 *
 * @since 2.0.0
 */
export declare namespace Exit {
  /**
   * Base interface shared by both Success and Failure.
   *
   * **When to use**
   *
   * Use to describe the common protocol implemented by every `Exit` value.
   *
   * **Details**
   *
   * Every Exit is also an Effect, so you can yield it in `Effect.gen`.
   *
   * @category models
   * @since 4.0.0
   */
  export interface Proto<out A, out E = never> extends Effect.Effect<A, E> {
    readonly [TypeId]: typeof TypeId
  }
}

/**
 * A successful Exit containing a value.
 *
 * **When to use**
 *
 * Use when working with the successful branch of an `Exit` after narrowing
 * with {@link isSuccess}. Access the value via the `value` property after
 * narrowing.
 *
 * **Example** (Accessing the success value)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const success = Exit.succeed(42)
 *
 * if (Exit.isSuccess(success)) {
 *   console.log(success._tag) // "Success"
 *   console.log(success.value) // 42
 * }
 * ```
 *
 * @see {@link isSuccess} to narrow an Exit to Success
 * @see {@link Failure} for the failure counterpart
 *
 * @category models
 * @since 2.0.0
 */
export interface Success<out A, out E = never> extends Exit.Proto<A, E> {
  readonly _tag: "Success"
  readonly value: A
}

/**
 * A failed Exit containing a Cause.
 *
 * **When to use**
 *
 * Use when working with the failed branch of an `Exit` after narrowing with
 * {@link isFailure}. Access the cause via the `cause` property after
 * narrowing.
 *
 * **Details**
 *
 * The `Cause<E>` may contain typed errors, defects, or interruptions.
 *
 * **Example** (Accessing the failure cause)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const failure = Exit.fail("something went wrong")
 *
 * if (Exit.isFailure(failure)) {
 *   console.log(failure._tag) // "Failure"
 *   console.log(failure.cause) // Cause representing the error
 * }
 * ```
 *
 * @see {@link isFailure} to narrow an Exit to Failure
 * @see {@link Success} for the success counterpart
 *
 * @category models
 * @since 2.0.0
 */
export interface Failure<out A, out E> extends Exit.Proto<A, E> {
  readonly _tag: "Failure"
  readonly cause: Cause.Cause<E>
}

/**
 * Checks whether an unknown value is an Exit.
 *
 * **When to use**
 *
 * Use to validate unknown values at system boundaries and narrow them to
 * `Exit<unknown, unknown>`.
 *
 * **Details**
 *
 * Does not inspect the contents of the Exit. Returns `true` for both Success
 * and Failure exits.
 *
 * **Example** (Checking if a value is an Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.isExit(Exit.succeed(42))) // true
 * console.log(Exit.isExit(Exit.fail("err"))) // true
 * console.log(Exit.isExit("not an exit"))    // false
 * ```
 *
 * @see {@link isSuccess} to check for a successful Exit
 * @see {@link isFailure} to check for a failed Exit
 *
 * @category guards
 * @since 2.0.0
 */
export const isExit: (u: unknown) => u is Exit<unknown, unknown> = core.isExit

/**
 * Creates a successful Exit containing the given value.
 *
 * **When to use**
 *
 * Use when you need an Exit that contains a known success value.
 *
 * **Details**
 *
 * Returns a `Success<A>` with the provided value. Does not perform any
 * computation.
 *
 * **Example** (Creating a successful Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.succeed(42)
 * console.log(Exit.isSuccess(exit)) // true
 * ```
 *
 * @see {@link fail} to create a failed Exit
 * @see {@link void_ void} for a pre-allocated success with no value
 *
 * @category constructors
 * @since 2.0.0
 */
export const succeed: <A>(a: A) => Exit<A> = core.exitSucceed

/**
 * Creates a failed Exit from a Cause.
 *
 * **When to use**
 *
 * Use when you already have a `Cause<E>` and want to wrap it in an Exit
 * for advanced error handling where you need full control over the Cause
 * structure.
 *
 * **Details**
 *
 * Returns a `Failure<never, E>`. If you only have an error value, use
 * {@link fail} instead.
 *
 * **Example** (Creating a failed Exit from a Cause)
 *
 * ```ts
 * import { Cause, Exit } from "effect"
 *
 * const cause = Cause.fail("Something went wrong")
 * const exit = Exit.failCause(cause)
 * console.log(Exit.isFailure(exit)) // true
 * ```
 *
 * @see {@link fail} to create a Failure from a plain error value
 * @see {@link die} to create a Failure from a defect
 *
 * @category constructors
 * @since 2.0.0
 */
export const failCause: <E>(cause: Cause.Cause<E>) => Exit<never, E> = core.exitFailCause

/**
 * Creates a failed Exit from a typed error value.
 *
 * **When to use**
 *
 * Use when you need to represent an expected typed failure as an `Exit`.
 *
 * **Details**
 *
 * The error is wrapped in a `Cause.Fail` internally.
 *
 * Returns a `Failure<never, E>`.
 *
 * **Example** (Creating a failed Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.fail("Something went wrong")
 * console.log(Exit.isFailure(exit)) // true
 * ```
 *
 * @see {@link succeed} to create a successful Exit
 * @see {@link die} to create a Failure from an unexpected defect
 * @see {@link failCause} to create a Failure from a full Cause
 *
 * @category constructors
 * @since 2.0.0
 */
export const fail: <E>(e: E) => Exit<never, E> = core.exitFail

/**
 * Creates a failed Exit from a defect (unexpected error).
 *
 * **When to use**
 *
 * Use when you need unexpected, unrecoverable errors that should not appear in
 * the typed error channel.
 *
 * **Details**
 *
 * The defect is wrapped in a `Cause.Die` internally.
 *
 * Returns a `Failure<never>` with `E = never`, since defects do not appear in
 * the typed error channel.
 *
 * **Example** (Creating a defect Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.die(new Error("Unexpected error"))
 * console.log(Exit.isFailure(exit)) // true
 * ```
 *
 * @see {@link fail} to create a Failure from a typed error
 * @see {@link hasDies} to check whether an Exit contains defects
 *
 * @category constructors
 * @since 2.0.0
 */
export const die: (defect: unknown) => Exit<never> = core.exitDie

/**
 * Creates a failed Exit representing fiber interruption.
 *
 * **When to use**
 *
 * Use to signal that a fiber was interrupted.
 *
 * **Details**
 *
 * Optionally pass a fiber ID to identify which fiber was interrupted. Returns
 * a `Failure<never>` with an `Interrupt` cause.
 *
 * **Example** (Creating an interruption Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.interrupt(123)
 * console.log(Exit.isFailure(exit)) // true
 * console.log(Exit.hasInterrupts(exit)) // true
 * ```
 *
 * @see {@link hasInterrupts} to check whether an Exit contains interruptions
 *
 * @category constructors
 * @since 2.0.0
 */
export const interrupt: (fiberId?: number | undefined) => Exit<never> = effect.exitInterrupt

const void_: Exit<void> = effect.exitVoid
export {
  /**
   * Provides a pre-allocated successful Exit with a `void` value.
   *
   * **When to use**
   *
   * Use when you need a shared successful `Exit` with no meaningful value.
   *
   * **Details**
   *
   * Equivalent to `Exit.succeed(undefined)` but shared as a single instance,
   * avoiding allocation for a common case.
   *
   * **Example** (Referencing the void Exit)
   *
   * ```ts
   * import { Exit } from "effect"
   *
   * const exit = Exit.void
   * console.log(Exit.isSuccess(exit)) // true
   * ```
   *
   * @see {@link succeed} to create a success with a specific value
   * @see {@link asVoid} to discard the value of an existing Exit
   *
   * @category constructors
   * @since 2.0.0
   */
  void_ as void
}

/**
 * Checks whether an Exit is a Success.
 *
 * **When to use**
 *
 * Use as a type guard to narrow `Exit<A, E>` to `Success<A, E>` and access the
 * `value` property.
 *
 * **Example** (Narrowing to success)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.succeed(42)
 *
 * if (Exit.isSuccess(exit)) {
 *   console.log(exit.value) // 42
 * }
 * ```
 *
 * @see {@link isFailure} for the opposite check
 * @see {@link match} for exhaustive pattern matching
 *
 * @category guards
 * @since 2.0.0
 */
export const isSuccess: <A, E>(self: Exit<A, E>) => self is Success<A, E> = effect.exitIsSuccess

/**
 * Checks whether an Exit is a Failure.
 *
 * **When to use**
 *
 * Use as a type guard to narrow `Exit<A, E>` to `Failure<A, E>` and access the
 * `cause` property.
 *
 * **Example** (Narrowing to failure)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.fail("error")
 *
 * if (Exit.isFailure(exit)) {
 *   console.log(exit.cause)
 * }
 * ```
 *
 * @see {@link isSuccess} for the opposite check
 * @see {@link match} for exhaustive pattern matching
 *
 * @category guards
 * @since 2.0.0
 */
export const isFailure: <A, E>(self: Exit<A, E>) => self is Failure<A, E> = effect.exitIsFailure

/**
 * Checks whether a failed Exit contains typed errors (Fail reasons).
 *
 * **When to use**
 *
 * Use to distinguish typed failures from defects or interruptions.
 *
 * **Details**
 *
 * Returns `false` for successful exits. Only checks for `Fail` reasons in the
 * Cause. A Cause with only `Die` or `Interrupt` reasons returns `false`.
 *
 * **Example** (Checking for typed errors)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.hasFails(Exit.fail("err")))           // true
 * console.log(Exit.hasFails(Exit.die(new Error("bug")))) // false
 * console.log(Exit.hasFails(Exit.succeed(42)))            // false
 * ```
 *
 * @see {@link hasDies} to check for defects
 * @see {@link hasInterrupts} to check for interruptions
 *
 * @category guards
 * @since 4.0.0
 */
export const hasFails: <A, E>(self: Exit<A, E>) => self is Failure<A, E> = effect.exitHasFails

/**
 * Checks whether a failed Exit contains defects (Die reasons).
 *
 * **When to use**
 *
 * Use to check whether an `Exit` failure cause contains unexpected errors.
 *
 * **Details**
 *
 * Returns `false` for successful exits. Only checks for `Die` reasons in the
 * Cause. A Cause with only `Fail` or `Interrupt` reasons returns `false`.
 *
 * **Example** (Checking for defects)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.hasDies(Exit.die(new Error("bug")))) // true
 * console.log(Exit.hasDies(Exit.fail("err")))           // false
 * console.log(Exit.hasDies(Exit.succeed(42)))            // false
 * ```
 *
 * @see {@link hasFails} to check for typed errors
 * @see {@link hasInterrupts} to check for interruptions
 *
 * @category guards
 * @since 4.0.0
 */
export const hasDies: <A, E>(self: Exit<A, E>) => self is Failure<A, E> = effect.exitHasDies

/**
 * Checks whether a failed Exit contains interruptions (Interrupt reasons).
 *
 * **When to use**
 *
 * Use to check whether an `Exit` contains fiber interruption.
 *
 * **Details**
 *
 * Returns `false` for successful exits. Only checks for `Interrupt` reasons in
 * the Cause. A Cause with only `Fail` or `Die` reasons returns `false`.
 *
 * **Example** (Checking for interruptions)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.hasInterrupts(Exit.interrupt(1))) // true
 * console.log(Exit.hasInterrupts(Exit.fail("err")))  // false
 * console.log(Exit.hasInterrupts(Exit.succeed(42)))   // false
 * ```
 *
 * @see {@link hasFails} to check for typed errors
 * @see {@link hasDies} to check for defects
 *
 * @category guards
 * @since 4.0.0
 */
export const hasInterrupts: <A, E>(self: Exit<A, E>) => self is Failure<A, E> = effect.exitHasInterrupts

/**
 * Extracts the Success variant from an Exit as a Result.
 *
 * **When to use**
 *
 * Use when composing Exit checks with `Filter` or other `Result`-based
 * filtering APIs and you want the full Success wrapper.
 *
 * **Details**
 *
 * Returns `Result.succeed(success)` when the Exit is a Success, or
 * `Result.fail(failure)` with the original Failure otherwise.
 *
 * **Gotchas**
 *
 * This is not an `Option` accessor or an Effect failure. A failed extraction is
 * represented as data in the `Result` failure channel.
 *
 * **Example** (Filtering for success)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.succeed(42)
 * const result = Exit.filterSuccess(exit)
 *
 * console.log(Result.isSuccess(result)) // true
 * ```
 *
 * @see {@link filterFailure} for the inverse
 * @see {@link filterValue} to extract the raw value instead of the Success object
 *
 * @category filtering
 * @since 4.0.0
 */
export const filterSuccess: <A, E>(
  self: Exit<A, E>
) => Result.Result<Success<A>, Failure<never, E>> = effect.exitFilterSuccess

/**
 * Extracts the success value from an Exit as a Result.
 *
 * **When to use**
 *
 * Use when composing Exit checks with `Filter` or other `Result`-based
 * filtering APIs and you want the raw success value rather than the Success
 * wrapper.
 *
 * **Details**
 *
 * Returns `Result.succeed(value)` when the Exit is a Success, or
 * `Result.fail(failure)` with the original Failure otherwise.
 *
 * **Gotchas**
 *
 * This is not an `Option` accessor or an Effect failure. A failed extraction is
 * represented as data in the `Result` failure channel.
 *
 * **Example** (Filtering for the value)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.succeed(42)
 * const result = Exit.filterValue(exit)
 *
 * console.log(Result.isSuccess(result) && result.success) // 42
 * ```
 *
 * @see {@link filterSuccess} to get the full Success object
 * @see {@link getSuccess} to get the value as an Option instead
 *
 * @category filtering
 * @since 4.0.0
 */
export const filterValue: <A, E>(self: Exit<A, E>) => Result.Result<A, Failure<never, E>> = effect.exitFilterValue

/**
 * Extracts the Failure variant from an Exit as a Result.
 *
 * **When to use**
 *
 * Use when composing Exit checks with `Filter` or other `Result`-based
 * filtering APIs and you want the full Failure wrapper.
 *
 * **Details**
 *
 * Returns `Result.succeed(failure)` when the Exit is a Failure, or
 * `Result.fail(success)` with the original Success otherwise.
 *
 * **Gotchas**
 *
 * This is not an `Option` accessor or an Effect failure. A failed extraction is
 * represented as data in the `Result` failure channel.
 *
 * **Example** (Filtering for failure)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.fail("err")
 * const result = Exit.filterFailure(exit)
 *
 * console.log(Result.isSuccess(result)) // true
 * ```
 *
 * @see {@link filterSuccess} for the inverse
 * @see {@link filterCause} to extract the Cause directly
 *
 * @category filtering
 * @since 4.0.0
 */
export const filterFailure: <A, E>(self: Exit<A, E>) => Result.Result<Failure<never, E>, Success<A>> =
  effect.exitFilterFailure

/**
 * Extracts the Cause from a failed Exit as a Result.
 *
 * **When to use**
 *
 * Use when composing Exit checks with `Filter` or other `Result`-based
 * filtering APIs and you want the raw Cause rather than the Failure wrapper.
 *
 * **Details**
 *
 * Returns `Result.succeed(cause)` when the Exit is a Failure, or
 * `Result.fail(success)` with the original Success otherwise.
 *
 * **Gotchas**
 *
 * This is not an `Option` accessor or an Effect failure. A failed extraction is
 * represented as data in the `Result` failure channel.
 *
 * **Example** (Filtering for the cause)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.fail("err")
 * const result = Exit.filterCause(exit)
 *
 * console.log(Result.isSuccess(result)) // true
 * ```
 *
 * @see {@link filterFailure} to get the full Failure object
 * @see {@link getCause} to get the Cause as an Option instead
 *
 * @category filtering
 * @since 4.0.0
 */
export const filterCause: <A, E>(self: Exit<A, E>) => Result.Result<Cause.Cause<E>, Success<A>> = effect.exitFilterCause

/**
 * Extracts the first typed error value from a failed Exit as a Result.
 *
 * **When to use**
 *
 * Use when you need the first typed error from an `Exit` as a `Result` for
 * `Filter` or other `Result`-based filtering APIs.
 *
 * **Details**
 *
 * Returns `Result.succeed(error)` when the Cause contains a Fail reason, or
 * `Result.fail(exit)` with the original Exit otherwise.
 *
 * **Gotchas**
 *
 * Only finds the first Fail reason. If the Cause has multiple errors, the rest
 * are ignored.
 *
 * **Example** (Finding the first typed error)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.fail("not found")
 * const result = Exit.findError(exit)
 * console.log(Result.isSuccess(result) && result.success) // "not found"
 *
 * const defect = Exit.die(new Error("bug"))
 * const noError = Exit.findError(defect)
 * console.log(Result.isFailure(noError)) // true
 * ```
 *
 * @see {@link findErrorOption} to get the error as an Option instead
 * @see {@link findDefect} to find defects instead
 *
 * @category filtering
 * @since 4.0.0
 */
export const findError: <A, E>(input: Exit<A, E>) => Result.Result<E, Exit<A, E>> = effect.exitFindError

/**
 * Extracts the first defect from a failed Exit as a Result.
 *
 * **When to use**
 *
 * Use when you need the first defect from an `Exit` as a `Result` for
 * `Filter` or other `Result`-based filtering APIs.
 *
 * **Details**
 *
 * Returns `Result.succeed(defect)` when the Cause contains a Die reason, or
 * `Result.fail(exit)` with the original Exit otherwise.
 *
 * **Gotchas**
 *
 * Only finds the first Die reason. If the Cause has multiple defects, the rest
 * are ignored.
 *
 * **Example** (Finding the first defect)
 *
 * ```ts
 * import { Exit, Result } from "effect"
 *
 * const exit = Exit.die("boom")
 * const result = Exit.findDefect(exit)
 * console.log(Result.isSuccess(result) && result.success) // "boom"
 *
 * const typed = Exit.fail("err")
 * const noDefect = Exit.findDefect(typed)
 * console.log(Result.isFailure(noDefect)) // true
 * ```
 *
 * @see {@link findError} to find typed errors instead
 * @see {@link hasDies} to check for defects without extracting them
 *
 * @category filtering
 * @since 4.0.0
 */
export const findDefect: <A, E>(input: Exit<A, E>) => Result.Result<unknown, Exit<A, E>> = effect.exitFindDefect

/**
 * Pattern matches on an Exit, handling both success and failure cases.
 *
 * **When to use**
 *
 * Use when you need exhaustive handling of both `Exit` success and failure
 * outcomes.
 *
 * **Details**
 *
 * Calls `onSuccess` with the value if the Exit is a Success, and calls
 * `onFailure` with the Cause if the Exit is a Failure.
 *
 * **Example** (Matching on an Exit)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const success = Exit.succeed(42)
 *
 * const result = Exit.match(success, {
 *   onSuccess: (value) => `Got: ${value}`,
 *   onFailure: () => "Failed"
 * })
 * console.log(result) // "Got: 42"
 * ```
 *
 * @see {@link isSuccess} and {@link isFailure} for simple boolean checks
 *
 * @category pattern matching
 * @since 2.0.0
 */
export const match: {
  <A, E, X1, X2>(options: {
    readonly onSuccess: (a: NoInfer<A>) => X1
    readonly onFailure: (cause: Cause.Cause<NoInfer<E>>) => X2
  }): (self: Exit<A, E>) => X1 | X2
  <A, E, X1, X2>(
    self: Exit<A, E>,
    options: {
      readonly onSuccess: (a: A) => X1
      readonly onFailure: (cause: Cause.Cause<E>) => X2
    }
  ): X1 | X2
} = effect.exitMatch

/**
 * Transforms the success value of an Exit using the given function.
 *
 * **When to use**
 *
 * Use to apply a transformation to the value inside a successful Exit
 *
 * **Details**
 *
 * Failures pass through unchanged.
 *
 * Allocates a new Exit if successful.
 *
 * **Example** (Mapping over a success)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.succeed(21)
 * const doubled = Exit.map(exit, (x) => x * 2)
 * console.log(Exit.isSuccess(doubled) && doubled.value) // 42
 * ```
 *
 * @see {@link mapError} to transform the error
 * @see {@link mapBoth} to transform both success and error
 *
 * @category combinators
 * @since 2.0.0
 */
export const map: {
  <A, B>(f: (a: A) => B): <E>(self: Exit<A, E>) => Exit<B, E>
  <A, E, B>(self: Exit<A, E>, f: (a: A) => B): Exit<B, E>
} = effect.exitMap

/**
 * Transforms the typed error of a failed Exit using the given function.
 *
 * **When to use**
 *
 * Use to remap typed errors while preserving the Exit structure
 *
 * **Details**
 *
 * Successes pass through unchanged.
 *
 * Allocates a new Exit if the error is transformed.
 *
 * **Gotchas**
 *
 * Only transforms typed errors (Fail reasons). If the Cause contains only
 * defects or interruptions, the failure passes through unchanged.
 *
 * **Example** (Mapping over an error)
 *
 * ```ts
 * import { Data, Exit } from "effect"
 *
 * class ExitError extends Data.TaggedError("ExitError")<{ readonly input: string }> {}
 *
 * const exit = Exit.fail("bad input")
 * const mapped = Exit.mapError(exit, (e) => new ExitError({ input: e }))
 * console.log(Exit.isFailure(mapped)) // true
 * ```
 *
 * @see {@link map} to transform the success value
 * @see {@link mapBoth} to transform both success and error
 *
 * @category combinators
 * @since 2.0.0
 */
export const mapError: {
  <E, E2>(f: (a: NoInfer<E>) => E2): <A>(self: Exit<A, E>) => Exit<A, E2>
  <A, E, E2>(self: Exit<A, E>, f: (a: NoInfer<E>) => E2): Exit<A, E2>
} = effect.exitMapError

/**
 * Transforms both the success value and typed error of an Exit.
 *
 * **When to use**
 *
 * Use when you need to remap both channels in one step.
 *
 * **Details**
 *
 * `onSuccess` transforms the value if the Exit is a Success. `onFailure`
 * transforms the typed error if the Exit is a Failure with a Fail reason.
 * Allocates a new Exit.
 *
 * **Gotchas**
 *
 * If the Cause contains only defects or interruptions, the failure passes
 * through unchanged.
 *
 * **Example** (Mapping both channels)
 *
 * ```ts
 * import { Data, Exit } from "effect"
 *
 * class ExitError extends Data.TaggedError("ExitError")<{ readonly input: string }> {}
 *
 * const exit = Exit.succeed(42)
 * const mapped = Exit.mapBoth(exit, {
 *   onSuccess: (x) => String(x),
 *   onFailure: (e: string) => new ExitError({ input: e })
 * })
 * console.log(Exit.isSuccess(mapped) && mapped.value) // "42"
 * ```
 *
 * @see {@link map} to transform only the success value
 * @see {@link mapError} to transform only the error
 *
 * @category combinators
 * @since 2.0.0
 */
export const mapBoth: {
  <E, E2, A, A2>(
    options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 }
  ): (self: Exit<A, E>) => Exit<A2, E2>
  <A, E, E2, A2>(
    self: Exit<A, E>,
    options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 }
  ): Exit<A2, E2>
} = effect.exitMapBoth

/**
 * Discards the success value of an Exit, replacing it with `void`.
 *
 * **When to use**
 *
 * Use when you need to discard a successful `Exit` value while preserving
 * whether the `Exit` succeeded or failed.
 *
 * **Details**
 *
 * Failures pass through unchanged.
 *
 * Allocates a new Exit if successful.
 *
 * **Example** (Discarding the success value)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exit = Exit.succeed(42)
 * const voided = Exit.asVoid(exit)
 * console.log(Exit.isSuccess(voided)) // true
 * ```
 *
 * @see {@link void_ void} for a pre-allocated void success
 * @see {@link asVoidAll} to combine multiple exits into a single void Exit
 *
 * @category combinators
 * @since 2.0.0
 */
export const asVoid: <A, E>(self: Exit<A, E>) => Exit<void, E> = effect.exitAsVoid

/**
 * Combines multiple Exit values into a single `Exit<void, E>`.
 *
 * **When to use**
 *
 * Use to validate that all exits in a collection succeeded
 *
 * **Details**
 *
 * If all exits are successful, this returns a void success. If any exit is a
 * failure, this returns a single failure with all error causes combined.
 *
 * Iterates over the entire collection. Collects all failure causes, not just
 * the first.
 *
 * **Example** (Combining exits)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * const exits = [Exit.succeed(1), Exit.succeed(2), Exit.succeed(3)]
 * console.log(Exit.isSuccess(Exit.asVoidAll(exits))) // true
 *
 * const mixed = [Exit.succeed(1), Exit.fail("err"), Exit.succeed(3)]
 * console.log(Exit.isFailure(Exit.asVoidAll(mixed))) // true
 * ```
 *
 * @see {@link asVoid} to discard the value of a single Exit
 *
 * @category combinators
 * @since 4.0.0
 */
export const asVoidAll: <I extends Iterable<Exit<any, any>>>(
  exits: I
) => Exit<void, I extends Iterable<Exit<infer _A, infer _E>> ? _E : never> = effect.exitAsVoidAll

/**
 * Returns the success value of an Exit as an Option.
 *
 * **When to use**
 *
 * Use when you need the success value from an `Exit` as an `Option` instead of
 * pattern matching.
 *
 * **Details**
 *
 * Returns `Option.some(value)` for a Success and `Option.none()` for a Failure.
 *
 * **Example** (Getting the success value)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.getSuccess(Exit.succeed(42))) // { _tag: "Some", value: 42 }
 * console.log(Exit.getSuccess(Exit.fail("err"))) // { _tag: "None" }
 * ```
 *
 * @see {@link getCause} to extract the Cause of a failure
 * @see {@link filterValue} for filter-pipeline usage
 *
 * @category accessors
 * @since 4.0.0
 */
export const getSuccess: <A, E>(self: Exit<A, E>) => Option<A> = effect.exitGetSuccess

/**
 * Returns the Cause of a failed Exit as an Option.
 *
 * **When to use**
 *
 * Use when you need the failure `Cause` from an `Exit` as an `Option` instead
 * of pattern matching.
 *
 * **Details**
 *
 * Returns `Option.some(cause)` for a Failure and `Option.none()` for a Success.
 *
 * **Example** (Getting the failure cause)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.getCause(Exit.fail("err"))) // { _tag: "Some", value: ... }
 * console.log(Exit.getCause(Exit.succeed(42))) // { _tag: "None" }
 * ```
 *
 * @see {@link getSuccess} to extract the success value
 * @see {@link filterCause} for filter-pipeline usage
 *
 * @category accessors
 * @since 4.0.0
 */
export const getCause: <A, E>(self: Exit<A, E>) => Option<Cause.Cause<E>> = effect.exitGetCause

/**
 * Returns the first typed error from a failed Exit as an Option.
 *
 * **When to use**
 *
 * Use when you need the first typed error from an `Exit` as an `Option`,
 * ignoring successes and non-typed failures.
 *
 * **Details**
 *
 * Returns `Option.some(error)` if the Cause contains a Fail reason. Successes,
 * defect-only failures, and interrupt-only failures return `Option.none()`.
 *
 * **Gotchas**
 *
 * Only finds the first Fail reason. If the Cause has multiple typed errors,
 * the rest are ignored.
 *
 * **Example** (Getting the first error)
 *
 * ```ts
 * import { Exit } from "effect"
 *
 * console.log(Exit.findErrorOption(Exit.fail("err")))           // { _tag: "Some", value: "err" }
 * console.log(Exit.findErrorOption(Exit.die(new Error("bug")))) // { _tag: "None" }
 * console.log(Exit.findErrorOption(Exit.succeed(42)))            // { _tag: "None" }
 * ```
 *
 * @see {@link findError} for filter-pipeline usage
 * @see {@link getCause} to get the full Cause as an Option
 *
 * @category accessors
 * @since 4.0.0
 */
export const findErrorOption: <A, E>(self: Exit<A, E>) => Option<E> = effect.exitFindErrorOption
