import { Ok, Err, Result } from '@hazae41/result';
import { Awaitable } from '../../libs/promises/promises.js';
import { DataInit, Data } from './data.js';
import { FailInit, Fail } from './fail.js';
import { TimedInit, CachedInit } from './times.js';

type FetchedInit<D, F> = DataInit<D> | FailInit<F>;
declare namespace FetchedInit {
    type Infer<T> = DataInit.Infer<T> | FailInit.Infer<T>;
}
type Fetched<D, F> = Data<D> | Fail<F>;
declare namespace Fetched {
    type Infer<T> = Data.Infer<T> | Fail.Infer<T>;
    function from<T extends FetchedInit.Infer<T>>(init: T): Fetched<DataInit.Inner<T>, FailInit.Inner<T>>;
    function rewrap<T extends Ok.Infer<T>>(result: T & TimedInit & CachedInit, init?: TimedInit & CachedInit): Data<Ok.Inner<T>>;
    function rewrap<T extends Err.Infer<T>>(result: T & TimedInit & CachedInit, init?: TimedInit & CachedInit): Fail<Err.Inner<T>>;
    function rewrap<T extends Result.Infer<T>>(result: T & TimedInit & CachedInit, init?: TimedInit & CachedInit): Fetched<Ok.Inner<T>, Err.Inner<T>>;
    /**
     * Run a callback and wrap any returned value in Ok<T> and any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runAndWrap<T>(callback: () => Awaitable<T>, init?: TimedInit & CachedInit): Promise<Fetched<T, unknown>>;
    /**
     * Run a callback and wrap any returned value in Ok<T> and any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runAndWrapSync<T>(callback: () => T, init?: TimedInit & CachedInit): Fetched<T, unknown>;
    /**
     * Run a callback and wrap any returned value in Ok<T> and any thrown error in Err<Catched>
     * @param callback
     * @returns
     */
    function runAndDoubleWrap<T>(callback: () => Awaitable<T>, init?: TimedInit & CachedInit): Promise<Fetched<T, Error>>;
    /**
     * Run a callback and wrap any returned value in Ok<T> and any thrown error in Err<Catched>
     * @param callback
     * @returns
     */
    function runAndDoubleWrapSync<T>(callback: () => T, init?: TimedInit & CachedInit): Fetched<T, Error>;
    /**
     * Run a callback and wrap any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runOrWrap<F extends Fetched.Infer<F>>(callback: () => Awaitable<F>, init?: TimedInit & CachedInit): Promise<F | Fail<unknown>>;
    /**
     * Run a callback and wrap any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runOrWrapSync<F extends Fetched.Infer<F>>(callback: () => F, init?: TimedInit & CachedInit): F | Fail<unknown>;
    /**
     * Run a callback and wrap any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runOrDoubleWrap<F extends Fetched.Infer<F>>(callback: () => Awaitable<F>, init?: TimedInit & CachedInit): Promise<F | Fail<Error>>;
    /**
     * Run a callback and wrap any thrown error in Err<unknown>
     * @param callback
     * @returns
     */
    function runOrDoubleWrapSync<F extends Result.Infer<F>>(callback: () => F, init: TimedInit & CachedInit): F | Fail<Error>;
}

export { Fetched, FetchedInit };
