/**
 * # IronEnum
 * A **zero‑dependency** helper that brings *Rust‑like tagged unions* (aka algebraic data
 * types) to TypeScript **while staying 100 % runtime‑light**.  You get:
 *
 * * **Ergonomic, type‑safe constructors** – payload gets the right shape or you don’t compile.
 * * **Pattern‑matching helpers** (`match`, `matchAsync`).
 * * **Fluent guards** (`if.*`, `ifNot.*`).
 * * **Rust‑inspired convenience wrappers** – `Option`, `Result`, `Try`, `TryInto`.
 * * **Smart payload rules** – when *every* property of the payload object is optional the
 *   argument itself becomes optional, so `Enum.Variant()` and `Enum.Variant({})` are both
 *   legal.
 *
 * ```ts
 * const Status = IronEnum<{
 *   Loading: undefined
 *   Ready:   { finishedAt: Date }
 * }>();
 *
 * const a = Status.Loading();                  // no payload
 * const b = Status.Ready({ finishedAt: new Date() });
 *
 * console.log(a.match({
 *   Loading: () => "still working…",
 *   Ready:   ({ finishedAt }) => `done at ${finishedAt}`,
 * }));
 * ```
 *
 * ## Table of contents (public surface)
 * 1. `IronEnum` – generic builder
 * 2. `EnumFactory` / `EnumMethods` – runtime value API (guards + matchers)
 * 3. `Option`, `Some`, `None` – maybe‑value helpers
 * 4. `Result`, `Ok`, `Err` – success/error helpers
 * 5. `Try`, `TryInto` – convert imperative `throw`s into functional `Result`s
 *
 * ---------------------------------------------------------------------------
 */

/**
 * A record of possible variants, each key mapping to its associated data type.
 * 
 * Example:
 * ```ts
 * type OptionVariants = { Some: string, None: undefined };
 * 
 * // "Some" has an associated string, while "None" has no associated data (undefined).
 * ```
 */
export type VariantsRecord = {
    [K in Exclude<string, "_">]: any;
};

type EnumUnion<ALL extends VariantsRecord> = {
    [K in keyof ALL & string]: ALL[K];
}[keyof ALL & string];

export type EnumFactoryUnion<ALL extends VariantsRecord> = {
    [K in keyof ALL & string]: EnumFactory<K, ALL[K], ALL>;
}[keyof ALL & string];


/** `true` when `T` is an object *and* every key is optional */
type IsAllOptionalObject<T> =
    T extends object                    // must be object-like
    ? ({} extends T                   // `{}` satisfies it ⇒ every key is optional
        ? true
        : false)
    : false;


type VariantConstructor<
  Default,     
  K extends string,
  ALL extends VariantsRecord
> =
  /* no-data variants stay 0-arg */
  [Default] extends [undefined | null | void]
    ? () => EnumFactory<K, Default, ALL>

  /* all-optional object -> parameter *may* be omitted */
  : IsAllOptionalObject<Default> extends true
      ? <P extends Default = Default>(
            data?: P
        ) => EnumFactory<K, P, ALL>

  /* everything else - payload required */
  : <P extends Default>(
        data: P
    ) => EnumFactory<K, P, ALL>;

/**
 * Represents a single constructed enum value.
 * 
 * - `tag` is the literal name of the variant.
 * - `data` is the data associated with this variant (could be undefined).
 * - It intersects with all of the enum methods (`EnumMethods`).
 * 
 * Generics:
 * - `TAG`: the specific variant key.
 * - `PAYLOAD`: the associated data type for this key.
 * - `ALL`: the entire `VariantsRecord` for reference by the methods.
 */
export type EnumFactory<
    TAG extends keyof ALL & string,
    PAYLOAD,
    ALL extends VariantsRecord
> = {
    tag: TAG;
    data: EnumUnion<ALL>;
    payload: PAYLOAD;
} & EnumMethods<ALL>;

/**
 * A set of methods made available to every constructed enum value:
 * - `toJSON`: Returns a plain object with exactly one key/value corresponding to the current variant.
 * - `key`: Returns the variant key.
 * - `if` / `ifNot`: Provide conditional checks against a specific variant.
 * - `match` / `matchAsync`: Synchronously or asynchronously pattern-match on the variant key.
 */
export interface EnumMethods<ALL extends VariantsRecord> {
    /**
     * Returns a simple JavaScript object that has exactly one key, the variant name, 
     * and its associated data as the value.
     */
    toJSON: () => Partial<ALL>;
    /**
     * Returns the variant key for this enum value.
     */
    key: () => keyof ALL & string;
    /**
     * Provides an object with methods for handling a check against each variant:
     * 
     * ```ts
     * const value = MyEnum.Foo("some data");
     * 
     * value.if.Foo(
     *   (payload) => console.log("This is Foo with data:", payload),
     *   (jsonObj) => console.log("This is not Foo; got:", jsonObj)
     * );
     * ```
     */
    if: ObjectToIfMap<ALL>;
    /**
     * Provides an object with methods for handling the inverse check of a variant:
     * 
     * ```ts
     * const value = MyEnum.Foo("some data");
     * 
     * value.ifNot.Bar(
     *   (jsonObj) => console.log("Value is not Bar; got:", jsonObj),
     *   (jsonObj) => console.log("Value is Bar; got:", jsonObj)
     * );
     * ```
     */
    ifNot: ObjectToIfNotMap<ALL>;
    /**
     * Perform synchronous pattern matching on the variant. Must handle all variants or use `_` as a fallback.
     * 
     * ```ts
     * const result = MyEnum.Foo("some data").match({
     *   Foo: (payload) => `Got Foo with ${payload}`,
     *   Bar: (payload) => `Got Bar with ${payload}`,
     *   // fallback if not handled above
     *   _: () => "Unknown variant!"
     * });
     * ```
     */
    match: <A extends MatchFns<ALL>>(callbacks: A) => MatchResult<A>;
    /**
     * Similar to `match` but allows asynchronous callback functions.
     * 
     * ```ts
     * const result = await MyEnum.Foo("some data").matchAsync({
     *   Foo: async (payload) => await processFoo(payload),
     *   Bar: async (payload) => await processBar(payload),
     *   _: async () => await handleUnknown()
     * });
     * ```
     */
    matchAsync: <A extends MatchFnsAsync<ALL>>(callbacks: A) => Promise<MatchResult<A>>;
    // eq: (to: EnumFactory<keyof ALL & string, unknown, ALL>, mode: "tag" | "tag-data" ) => boolean
    // notEq: (to: EnumFactory<keyof ALL & string, unknown, ALL>, mode: "tag" | "tag-data" ) => boolean
}

type NonOptional<T> = {
    [K in keyof T]-?: T[K];
};

type ObjectToFunctionMapBase<T, R> = {
    [K in keyof T]?: T[K] extends undefined | null
    ? () => R
    : (args: T[K]) => R;
};

type ObjectToFunctionMap<T> = ObjectToFunctionMapBase<T, any>;
type ObjectToFunctionMapAsync<T> = ObjectToFunctionMapBase<T, Promise<any>>;

/**
 * Pattern matching map for each variant. Must specify all variant keys or have a "_" fallback.
 */
type MatchFns<X extends VariantsRecord> =
    | NonOptional<ObjectToFunctionMap<X>>
    | (ObjectToFunctionMap<X> & { _: () => any });

/**
 * Pattern matching map for each variant with async callbacks. Must specify all variant keys or have a "_" fallback.
 */
type MatchFnsAsync<X extends VariantsRecord> =
    | NonOptional<ObjectToFunctionMapAsync<X>>
    | (ObjectToFunctionMapAsync<X> & { _: () => Promise<any> });

/**
 * Derives the return type from the union of callback signatures in the map.
 */
type MatchResult<A> = A extends { [K: string]: (...args: any) => infer R }
    ? R
    : never;

/**
 * If the variant matches the key, call the `ifCallback`; otherwise call `elseCallback`.
 * The return type is designed so if your callbacks return `void`, 
 * it simply returns a boolean indicating the success/failure of the check.
 */
type IfFnNull<T extends Record<string, any>> = <
    RIf = void,
    RElse = void
>(
    ifCallback?: () => RIf,
    elseCallback?: (obj: Partial<T>) => RElse
) => [RIf, RElse] extends [void, void]
    ? boolean
    : RIf extends void
    ? boolean | Exclude<RElse, void>
    : RElse extends void
    ? boolean | Exclude<RIf, void>
    : Exclude<RIf, void> | Exclude<RElse, void>;

/**
 * Similar to `IfFnNull`, but for properties that are not null/undefined.
 * The `ifCallback` receives the associated data, e.g. the `payload`.
 */
type IfFnArg<TValue, T extends Record<string, any>> = <
    RIf = void,
    RElse = void
>(
    ifCallback?: (val: TValue) => RIf,
    elseCallback?: (jsonValue: Partial<T>) => RElse
) => [RIf, RElse] extends [void, void]
    ? boolean
    : RIf extends void
    ? boolean | Exclude<RElse, void>
    : RElse extends void
    ? boolean | Exclude<RIf, void>
    : Exclude<RIf, void> | Exclude<RElse, void>;

/**
 * The object type used by the `if` property on an enum instance.
 * Each key corresponds to a potential variant key, returning a function 
 * that handles the if/else logic for that variant.
 */
type ObjectToIfMap<T extends Record<string, any>> = {
    [K in keyof T]: T[K] extends null | undefined
    ? IfFnNull<T>
    : IfFnArg<T[K], T>;
};

/**
 * `ifNot` logic handles the inverse check of a given key. 
 * The callback is invoked if the variant is NOT the specified key, 
 * else the `elseCallback` is invoked.
 */
type IfNotFn<TAll> = <
    RIf = void,
    RElse = void
>(
    callback?: (jsonValue: Partial<TAll>) => RIf,
    elseCallback?: (jsonValue: Partial<TAll>) => RElse
) => [RIf, RElse] extends [void, void]
    ? boolean
    : RIf extends void
    ? boolean | Exclude<RElse, void>
    : RElse extends void
    ? boolean | Exclude<RIf, void>
    : Exclude<RIf, void> | Exclude<RElse, void>;

/**
 * The object type used by the `ifNot` property on an enum instance.
 * Each key corresponds to a potential variant key, returning a function 
 * that handles the "not that variant" logic.
 */
type ObjectToIfNotMap<T> = {
    [K in keyof T]: IfNotFn<T>;
};

/**
 * Creates a single variant object internally, with all the associated utility methods.
 * 
 * @param allVariants - The entire variants record for reference (not used directly, but typed).
 * @param tag - The variant key being constructed.
 * @param data - The associated data for this variant key.
 * @returns An object with `tag`, `data`, and all the utility methods (`if`, `ifNot`, `match`, etc.).
 */
function enumFactory<
    ALL extends VariantsRecord,
    TAG extends keyof ALL & string
>(
    allVariants: ALL,
    tag: TAG,
    data: ALL[TAG]
): EnumFactory<TAG, ALL[TAG], ALL> {

    if (tag === "_") {
        throw new Error(
            'Variant key "_" is reserved; cannot use "_" as a variant name.'
        );
    }

    return {
        tag,
        data: data,
        payload: data,
        toJSON: () => ({ [tag]: data } as unknown as Partial<ALL>),
        key: () => tag,
        if: new Proxy({} as ObjectToIfMap<ALL>, {
            get: (_tgt, prop: string) => {
                return (callback?: Function, elseCallback?: Function) => {
                    if (prop === tag) {
                        if (callback) {
                            const result = callback(data);
                            return result === undefined ? true : result;
                        }
                        return true;
                    } else if (elseCallback) {
                        const result = elseCallback({ [tag]: data } as unknown as Partial<ALL>);
                        return result === undefined ? false : result;
                    }
                    return false;
                };
            }
        }),
        ifNot: new Proxy({} as ObjectToIfNotMap<ALL>, {
            get: (_tgt, prop: string) => {
                return (callback?: Function, elseCallback?: Function) => {
                    if (prop !== tag) {
                        if (callback) {
                            const result = callback({ [tag]: data } as unknown as Partial<ALL>);
                            return result === undefined ? true : result;
                        }
                        return true;
                    } else if (elseCallback) {
                        const result = elseCallback({ [tag]: data } as unknown as Partial<ALL>);
                        return result === undefined ? false : result;
                    }
                    return false;
                };
            }
        }),
        match: (callbacks) => {
            const maybeFn = callbacks[tag];
            if (maybeFn) {
                return maybeFn(data);
            }
            const catchAll = callbacks._ as () => any;
            if (catchAll) {
                return catchAll();
            }
            throw new Error(
                `No handler for variant "${String(tag)}" and no "_" fallback`
            );
        },
        matchAsync: async (callbacks) => {
            const maybeFn = callbacks[tag];
            if (maybeFn) {
                return await maybeFn(data);
            }
            const catchAll = callbacks._ as () => Promise<any>;
            if (catchAll) {
                return await catchAll();
            }
            throw new Error(
                `No handler for variant "${String(tag)}" and no "_" fallback`
            );
        }
    };
}

/**
 * Utility type that captures additional properties for a given Enum,
 * including parse and type-checking utilities.
 */
export type EnumProperties<ALL extends VariantsRecord, AddedProps> = {
    /**
     * The variant keys available on this enum.
     */
    typeKeys: keyof ALL,
    /**
     * The variants record used to construct this enum.
     */
    typeVariants: Partial<ALL>,
    /**
     * The type of the Enum for usage in function arguments, etc.
     * 
     * Example:
     * ```ts
     * const MyEnum = IronEnum<{ Foo: string, Bar: number }>();
     * // The type of the entire enum instance:
     * type MyEnumType = typeof MyEnum._.typeOf;
     * 
     * function doSomething(value: MyEnumType) { ... }
     * ```
     */
    typeOf: EnumFactoryUnion<ALL> & AddedProps,
    /**
     * Reconstructs a variant from a plain object that must have exactly one key.
     * 
     * Example:
     * ```ts
     * const dataObj = { Foo: "hello" };
     * const myVariant = MyEnum._.parse(dataObj); 
     * // myVariant is an enum value with tag="Foo" and data="hello"
     * ```
     */
    parse(dataObj: Partial<ALL>): EnumFactory<keyof ALL & string, EnumUnion<ALL>, ALL>;
};

/**
 * The shape of the object returned by `IronEnum`. It has one method per variant key
 * that can construct that variant. It also has a special `_` property 
 * which is an object containing additional helpers (like `parse`).
 */
export type IronEnumInstance<ALL extends VariantsRecord> = {
    /**
     * For a variant key that has associated data, call it like `MyEnum.Foo("myData")`.
     * If the variant has no data, call it like `MyEnum.None()`.
     */
    [K in keyof ALL & string]: VariantConstructor<ALL[K], K, ALL>;
} & {
    /**
     * A special property containing meta-information and helper methods for the enum, 
     * such as `parse(...)`.
     */
    _: EnumProperties<ALL, {}>
};

/**
 * Constructs an enum "builder" object. 
 * 
 * Each key in the `ALL` record becomes a function to produce that variant value.
 * 
 * Usage Example:
 * ```ts
 * const MyEnum = IronEnum<{ Foo: string, Bar: number }>();
 * const fooValue = MyEnum.Foo("some text");
 * const barValue = MyEnum.Bar(123);
 * 
 * ```
 * 
 * Note: Do not use the "_" key in your variants — it is reserved for catch-all logic.
 */
export function IronEnum<ALL extends VariantsRecord>(): "_" extends keyof ALL ? "ERROR: Cannot use '_' as a variant key!" : IronEnumInstance<ALL> {

    // Using a Proxy to dynamically handle variant construction
    // and the special "parse" method at runtime.
    return new Proxy({}, {
        get: (_tgt, prop: string) => {
            if (prop === "_") {
                return new Proxy({}, {
                    get: (_tgt2, prop2: string) => {
                        if (prop2 == "parse") {
                            return (dataObj: Partial<ALL>) => {
                                const keys = Object.keys(dataObj);
                                if (keys.length !== 1) {
                                    throw new Error(
                                        `Expected exactly 1 variant key, got ${keys.length}`
                                    );
                                }
                                const actualKey = keys[0] as keyof ALL & string;
                                return enumFactory<ALL, typeof actualKey>(
                                    {} as ALL,
                                    actualKey,
                                    dataObj[actualKey] as ALL[typeof actualKey]
                                );
                            };
                        }
                        throw new Error(`Property '${prop2}' not availalbe at runtime!`);
                    }
                });
            }

            return (...args: [any?]) => {
                // if caller omitted the argument *and* the payload type is an object,
                // inject an empty object so runtime matches the static type
                const data =
                    args.length === 0 ? ({} as ALL[typeof prop]) : (args[0] as ALL[typeof prop]);

                return enumFactory<ALL, typeof prop>({} as ALL, prop, data);
            };
        }
    }) as any;
}

/**
 * Extends the basic enum instance with Rust-like methods for retrieving or transforming data.
 * - `unwrap()`, `unwrap_or()`, `unwrap_or_else()`, `_try()`.
 */
type ExtendedRustMethods<T> = {
    /**
     * If this is a success/some variant, return the underlying data.
     * Otherwise, throw an Error.
     */
    unwrap: () => T,
    /**
     * If this is a success/some variant, return the data.
     * Otherwise, return the provided default `value`.
     */
    unwrap_or: <R>(value: R) => R | T,
    /**
     * If this is a success/some variant, return the data.
     * Otherwise, return the result of `callback()`.
     */
    unwrap_or_else: <R>(callback: () => R) => R | T,
    // _try: () => any
};

/**
 * Extends a Result type with an `ok()` method that transforms `Ok` data into a `Some` Option, 
 * or `Err` into a `None`.
 */
type ResultMethods<ALL extends { Ok: unknown, Err: unknown }> = {
    /**
     * Converts an `Ok` variant into `Some`, or an `Err` variant into `None`.
     */
    ok: () => OptionFactory<{ Some: ALL["Ok"], None: undefined }>,
    /**
     * Returns if the current Result is Ok.
     */
    isOk: () => boolean,
    /**
     * Returns if the current Result is Err.
     */
    isErr: () => boolean
};

/**
 * A specific type for a `Result`-style enum, with Rust-like methods for success/error handling.
 * 
 * Example usage:
 * ```ts
 * const MyResult = Result<number, Error>();
 * const success = MyResult.Ok(42);
 * const failure = MyResult.Err(new Error("Oops"));
 * 
 * success.unwrap() // 42
 * failure.unwrap() // throws Error
 * ```
 */
export type ResultFactory<ALL extends { Ok: unknown, Err: unknown }> =
    EnumFactory<keyof ALL & string, EnumUnion<ALL>, ALL>
    & ExtendedRustMethods<ALL["Ok"]>
    & ResultMethods<ALL>;

/**
 * Returned by `Result<T, E>()`, it provides two methods for construction:
 * - `Ok(...)`
 * - `Err(...)`
 * and a `._` property for parsing, plus all the extended Rust methods in the type definition.
 */
export type ResultInstance<ALL extends { Ok: unknown, Err: unknown }> = {
    /**
     * Constructs a successful variant with the provided data.
     */
    Ok: (data: ALL["Ok"]) => ResultFactory<ALL>,
    /**
     * Constructs an error variant with the provided error data.
     */
    Err: (data: ALL["Err"]) => ResultFactory<ALL>
} & {
    /**
     * Provides the meta-information and parse function for the enum, 
     * as well as the extended Rust methods in the type signature.
     */
    _: EnumProperties<ALL, ExtendedRustMethods<ALL["Ok"]> & ResultMethods<ALL>>
};

/**
 * A Result type constructor for a success or error scenario, similar to Rust's `Result<T, E>`.
 * 
 * Example usage:
 * ```ts
 * const NumResult = Result<number, string>();
 * const okVal = NumResult.Ok(42);
 * console.log(okVal.unwrap()); // 42
 * 
 * const errVal = NumResult.Err("something happened");
 * console.log(errVal.unwrap_or(0)); // 0
 * ```
 */
export const Result = <T, E>(): ResultInstance<{ Ok: T, Err: E }> => (() => {
    const resultEnum = IronEnum<{ Ok: T, Err: E }>();

    return {
        _: resultEnum._ as any,
        Ok: (value: T) => ({
            ...resultEnum.Ok(value),
            unwrap: () => value,
            unwrap_or: x => value,
            isOk: () => true,
            isErr: () => false,
            unwrap_or_else: x => value,
            ok: () => Option<T>().Some(value)
        }),
        Err: (value: E) => ({
            ...resultEnum.Err(value),
            unwrap: () => {
                if (value instanceof Error) {
                    throw value;
                } else if (typeof value == "string") {
                    throw new Error(value);
                } else if (typeof value !== "undefined" && value !== null && typeof value.toString == "function") {
                    throw new Error(value.toString());
                } else {
                    throw new Error(`Called .unwrap() on an Result.Err enum!`);
                }
            },
            isOk: () => false,
            isErr: () => true,
            unwrap_or: x => x,
            unwrap_or_else: x => x(),
            ok: () => Option<T>().None()
        })
    };
})();

/**
 * A convenience function to build a `Result.Ok` variant inline, useful when you only care about a single Ok type.
 * 
 * ```ts
 * const val = Ok(123);
 * console.log(val.unwrap()); // 123
 * ```
 */
export const Ok = <T>(value: T): ResultFactory<{ Ok: T, Err: unknown }> => Result<T, unknown>().Ok(value);

/**
 * A convenience function to build a `Result.Err` variant inline, useful when you only care about a single Err type.
 * 
 * ```ts
 * const errorVal = Err("something went wrong");
 * console.log(errorVal.match({
 *   Ok: (v) => `Got OK with value ${v}`,
 *   Err: (e) => `Got Err: ${e}`
 * }));
 * ```
 */
export const Err = <E>(error: E): ResultFactory<{ Ok: unknown, Err: E }> => Result<unknown, E>().Err(error);

/**
 * A specialized type for an Option-like enum, with Rust-like methods. 
 * Extends the base enum with `ok_or` / `ok_or_else` that transform an Option into a Result.
 */
export type OptionFactory<ALL extends { Some: unknown, None: undefined }> =
    EnumFactory<keyof ALL & string, EnumUnion<ALL>, ALL>
    & ExtendedRustMethods<ALL["Some"]>
    & OptionMethods<ALL["Some"]>;

type OptionMethods<OK> = {
    /**
     * If the variant is `Some`, return `Result.Ok(value)`. Otherwise `Result.Err(error)`.
     */
    ok_or: <E>(error: E) => ResultFactory<{ Ok: OK, Err: E }>,
    /**
     * Similar to `ok_or`, but takes a function that returns the error.
     */
    ok_or_else: <E>(error: () => E) => ResultFactory<{ Ok: OK, Err: E }>
    /**
     * Returns is Option value is Some.
     */
    isSome: () => boolean,
    /**
     * Returns is Option value is None
     */
    isNone: () => boolean,
};

/**
 * Returned by `Option<T>()`, it provides two methods for construction:
 * - `Some(...)`
 * - `None()`
 * and a `._` property for parsing, plus all the extended Rust methods in the type definition.
 */
export type OptionInstance<ALL extends { Some: unknown, None: undefined }> = {
    /**
     * Construct a `Some` variant with associated data.
     */
    Some: (data: ALL["Some"]) => OptionFactory<ALL>,
    /**
     * Construct a `None` variant with no associated data.
     */
    None: () => OptionFactory<ALL>,
} & {
    /**
     * Provides the meta-information and parse function for the enum, 
     * as well as the extended Rust methods in the type signature.
     */
    _: EnumProperties<ALL, ExtendedRustMethods<ALL["Some"]> & OptionMethods<ALL["Some"]>>
};

/**
 * An Option type constructor for a possibly-undefined value, similar to Rust's `Option<T>`.
 * 
 * Example usage:
 * ```ts
 * const NumOption = Option<number>();
 * 
 * const someVal = NumOption.Some(123);
 * console.log(someVal.unwrap()); // 123
 * 
 * const noneVal = NumOption.None();
 * console.log(noneVal.unwrap_or(0)); // 0
 * ```
 */
export const Option = <T>(): OptionInstance<{ Some: T, None: undefined }> => (() => {
    const optEnum = IronEnum<{ Some: T, None: undefined }>();

    return {
        _: optEnum._ as any,
        Some: (value: T) => ({
            ...optEnum.Some(value),
            isSome: () => true,
            isNone: () => false,
            unwrap: () => value,
            unwrap_or: x => value,
            unwrap_or_else: x => value,
            ok_or: <R>(err: R) => Result<T, R>().Ok(value),
            ok_or_else: <R>(err: () => R) => Result<T, R>().Ok(value),
            _try: () => null
        }),
        None: () => ({
            ...optEnum.None(),
            isSome: () => false,
            isNone: () => true,
            unwrap: () => {
                throw new Error(`Called .unwrap() on an Option.None enum!`);
            },
            unwrap_or: x => x,
            unwrap_or_else: x => x(),
            ok_or: <R>(err: R) => Result<T, R>().Err(err),
            ok_or_else: <R>(err: () => R) => Result<T, R>().Err(err()),
            _try: () => null
        })
    };
})();

/**
 * A convenience function to construct `Option.Some` inline with a given value.
 * 
 * ```ts
 * const s = Some("Hello");
 * console.log(s.unwrap()); // "Hello"
 * ```
 */
export const Some = <T>(value: T): OptionFactory<{ Some: T, None: undefined }> => Option<T>().Some(value);

/**
 * A convenience function to construct `Option.None` inline with no associated data.
 * 
 * ```ts
 * const n = None();
 * console.log(n.unwrap_or("default")); // "default"
 * ```
 */
export const None = (): OptionFactory<{ Some: unknown, None: undefined }> => Option().None();


/**
 * A utility for wrapping function calls (both synchronous and asynchronous)
 * in a `Result` type, capturing successful outputs or caught exceptions.
 * 
 * This helps simplify error handling and improves readability by avoiding
 * `try/catch` blocks scattered throughout your code.
 * 
 * ## Example (Sync):
 * ```ts
 * const result = Try.sync(() => riskyOperation());
 * if (result.if.Ok()) {
 *     console.log("Success:", result.value);
 * } else {
 *     console.error("Error:", result.error);
 * }
 * ```
 * 
 * ## Example (Async):
 * ```ts
 * const result = await Try.async(() => fetchData());
 * if (result.if.Ok()) {
 *     console.log("Fetched:", result.value);
 * } else {
 *     console.error("Fetch failed:", result.error);
 * }
 * ```
 * 
 * @property sync - Wraps a synchronous function and returns a `ResultFactory`
 *                  with `Ok` or `Err` depending on whether an exception is thrown.
 * 
 * @property async - Wraps an asynchronous function (returning a Promise) and
 *                   returns a `Promise<ResultFactory>` containing the result or error.
 */
export const Try = {
    sync: <X>(callback: () => X): ResultFactory<{ Ok: X, Err: unknown }> => {
        try {
            const output = callback();
            return Result<X, unknown>().Ok(output);
        } catch (e: any) {
            return Result<X, unknown>().Err(e as unknown)
        }
    },
    async: async <X>(callback: () => Promise<X>): Promise<ResultFactory<{ Ok: X, Err: unknown }>> => {
        try {
            const output = await callback();
            return Result<X, unknown>().Ok(output);
        } catch (e: any) {
            return Result<X, unknown>().Err(e as unknown)
        }
    }
}

/**
 * A higher-order utility that converts a function (sync or async) into one that
 * returns a `Result` instead of throwing exceptions. This is especially useful
 * when you want to consistently return a safe result wrapper from potentially
 * failing operations, while preserving the original function signature.
 *
 * This differs from `Try` by returning a **new function** rather than calling
 * the function immediately.
 *
 * ## Example (Sync):
 * ```ts
 * const parseIntSafe = TryInto.sync((str: string) => {
 *   const num = parseInt(str);
 *   if (isNaN(num)) throw new Error("Invalid number");
 *   return num;
 * });
 *
 * const result = parseIntSafe("42");
 * if (result.if.Ok()) {
 *  console.log(result.value);
 * } else {
 *  console.error(result.error)
 * }
 * ```
 *
 * ## Example (Async):
 * ```ts
 * const fetchSafe = TryInto.async(async (url: string) => {
 *   const res = await fetch(url);
 *   if (!res.ok) throw new Error("Bad response");
 *   return res.json();
 * });
 *
 * const result = await fetchSafe("/api/data");
 * if (result.if.Ok()) {
 *  console.log(result.value);
 * } else {
 *  console.log(result.error);
 * }
 * ```
 *
 * @property sync - Wraps a synchronous function and returns a new function
 *                  that returns a `Result` instead of throwing.
 * @property async - Wraps an asynchronous function and returns a new function
 *                   that returns a `Promise<Result>` instead of rejecting.
 */
export const TryInto = {
    sync: <X, Y extends any[]>(callback: (...args: Y) => X): ((...args: Y) => ResultFactory<{ Ok: X, Err: unknown }>) => {
        return (...args: Y) => {
            try {
                const output = callback(...args);
                return Result<X, unknown>().Ok(output);
            } catch (e: any) {
                return Result<X, unknown>().Err(e as unknown)
            }
        }
    },
    async: <X, Y extends any[]>(callback: (...args: Y) => Promise<X>): ((...args: Y) => Promise<ResultFactory<{ Ok: X, Err: unknown }>>) => {
        return async (...args: Y) => {
            try {
                const output = await callback(...args);
                return Result<X, unknown>().Ok(output);
            } catch (e: any) {
                return Result<X, unknown>().Err(e as unknown)
            }
        }
    }
}