import { Ok, Err, Result } from 'neverthrow';
import { Match } from '../node_modules/ts-pattern/dist/types/Match.js';
import { unset } from '../node_modules/ts-pattern/dist/internals/symbols.js';
import { P } from 'ts-pattern';

type UnwrapOk<T> = T extends Ok<infer U, any> ? U : never;
type UnwrapErr<T> = T extends Err<any, infer U> ? U : never;
type WithDefault<a, b> = [a] extends [never] ? b : a;
/**
 * Extracts the Ok variant from a Result type, preserving the specific success type.
 * Converts `Result<T1 | T2, E>` into `Ok<T1, any> | Ok<T2, any>`.
 * This helps with precise type matching in pattern matching.
 */
type DistributeOkVariant<T> = T extends Ok<infer U, any> ? U extends any ? Ok<U, any> : never : never;
/**
 * Extracts the Err variant from a Result type, preserving the specific error type.
 * Converts `Result<T, E1 | E2>` into `Err<any, E1> | Err<any, E2>`.
 * This helps with precise type matching in pattern matching.
 */
type DistributeErrVariant<T> = T extends Err<any, infer U> ? U extends any ? Err<any, U> : never : never;
/**
 * Distributes union types within a Result to their respective Ok and Err variants.
 * Converts `Result<T1 | T2, E1 | E2>` into `Ok<T1, any> | Ok<T2, any> | Err<any, E1> | Err<any, E2>`.
 * This enables precise pattern matching on specific variants of union types.
 */
type DistributeResultVariants<T> = DistributeOkVariant<T> | DistributeErrVariant<T>;

declare module "ts-pattern" {
    /**
     * This is a custom overload for the `match` function that allows you to match on `neverthrow` results.
     * It prepares Result types for better pattern matching by distributing union types to their respective Ok and Err variants.
     *
     * `match` creates a **pattern matching expression**.
     *  * Use `.with(pattern, handler)` to pattern match on the input.
     *  * Use `.exhaustive()` or `.otherwise(() => defaultValue)` to end the expression and get the result.
     *
     * This type fixer from neverthrow-pattern enables precise pattern matching on Result types from neverthrow.
     *
     * @see https://github.com/gvergnaud/ts-pattern#match
     *
     * @example
     *  const result: Result<string, Error> = ok("success");
     *
     *  return match(result)
     *    .with(NP.ok(), (ok) => `Success: ${ok.value}`)
     *    .with(NP.err(), (err) => `Error: ${err.error.message}`)
     *    .exhaustive();
     */
    function match<const input extends Result<any, any>>(value: input): Match<DistributeResultVariants<input>, unset>;
}

/**
 * `ok(pattern?)` is a pattern matching function for `Ok` values from neverthrow.
 * It can be used to match against the success value inside an `Ok` instance.
 *
 * When used without arguments, it matches any `Ok` value.
 * When used with a pattern, it matches `Ok` values where the inner value matches the provided pattern.
 *
 * @example
 * ```ts
 * match(result)
 *   .with(NP.ok(P.number), (value) => 'matches Ok with a number value')
 *   .with(NP.ok({ status: 'success' }), (value) => 'matches Ok with an object value')
 *   .with(NP.ok(), (value) => 'matches any Ok value')
 * ```
 *
 * @param pattern - Optional pattern to match against the inner value of the Ok
 * @returns A pattern that matches Ok values from neverthrow
 */
declare function ok<input>(): P.unstable_Matchable<{
    value: UnwrapOk<input>;
}, input>;
declare function ok<input, const pattern extends P.Pattern<WithDefault<UnwrapOk<input>, unknown>>>(pattern: pattern): P.unstable_Matchable<{
    value: P.narrow<UnwrapOk<input>, pattern>;
}, input, pattern>;
/**
 * `err(pattern?)` is a pattern matching function for `Err` values from neverthrow.
 * It can be used to match against the error value inside an `Err` instance.
 *
 * When used without arguments, it matches any `Err` value.
 * When used with a pattern, it matches `Err` values where the inner error value matches the provided pattern.
 *
 * @example
 * ```ts
 * match(result)
 *   .with(NP.err(P.string), (error) => 'matches Err with a string error')
 *   .with(NP.err({ code: 'NOT_FOUND' }), (error) => 'matches Err with a specific error object')
 *   .with(NP.err(), (error) => 'matches any Err value')
 * ```
 *
 * @param pattern - Optional pattern to match against the inner error value of the Err
 * @returns A pattern that matches Err values from neverthrow
 */
declare function err<input>(): P.unstable_Matchable<{
    error: UnwrapErr<input>;
}, input>;
declare function err<input, const pattern extends P.Pattern<WithDefault<UnwrapErr<input>, unknown>>>(pattern: pattern): P.unstable_Matchable<{
    error: P.narrow<UnwrapErr<input>, pattern>;
}, input, pattern>;

declare const patterns_err: typeof err;
declare const patterns_ok: typeof ok;
declare namespace patterns {
  export { patterns_err as err, patterns_ok as ok };
}

export { type DistributeErrVariant, type DistributeOkVariant, type DistributeResultVariants, patterns as NP, patterns as NeverthrowPattern, type UnwrapErr, type UnwrapOk, type WithDefault };
