interface Scheduler {
}

/**
 * Custom config for the Readable/Writable.
 */
interface Config<TValue = any> {
    /**
     * Compare two values. Default `Object.is`.
     * `false` to disable equality check.
     */
    readonly equal?: Equal<TValue> | false;
    /**
     * Name for debugging.
     */
    readonly name?: string;
    /**
     * A callback invoked when a value is needed to be disposed.
     *
     * A value is considered for disposal when:
     * - it is replaced by another value (a new value is set).
     * - the Readable is disposed.
     *
     * @param oldValue The value that is needed to be disposed.
     */
    readonly onDisposeValue?: (oldValue: TValue) => void;
}
type Disposer = () => void;
type Equal<TValue = any> = (newValue: TValue, oldValue: TValue) => boolean;
/**
 * A Readable is a reactive value that can be read and subscribed to.
 *
 * @category Readable
 */
interface Readable<TValue = any> {
    readonly name?: string;
    /**
     * A version representation of the value.
     * If two versions of a $ is not equal(`Object.is`), it means the `value` has changed (event if the `value` is equal).
     */
    readonly version: Version;
    /**
     * Indicates whether the Readable has been disposed.
     */
    readonly disposed: boolean;
    /**
     * Current value of the $.
     */
    readonly value: TValue;
    /**
     * Get current value.
     */
    get: () => TValue;
    /**
     * Subscribe to value changes without immediate emission.
     * @param subscriber
     * @param scheduler Optional scheduler to control when the subscriber is called.
     * @returns a disposer function that cancels the subscription
     */
    reaction(subscriber: Subscriber<TValue>, scheduler?: Scheduler): Disposer;
    /**
     * Subscribe to value changes with immediate emission.
     * @param subscriber
     * @param scheduler Optional scheduler to control when the subscriber is called.
     * @returns a disposer function that cancels the subscription
     */
    subscribe(subscriber: Subscriber<TValue>, scheduler?: Scheduler): Disposer;
    /**
     * Remove the given subscriber or all subscribers if no subscriber is provided.
     * @param subscriber Optional subscriber function to remove.
     * @param scheduler Optional scheduler associated with the subscriber.
     * If not provided, all subscribers will be removed.
     */
    unsubscribe(subscriber?: (...args: any[]) => any, scheduler?: Scheduler): void;
}
/**
 * An OwnedReadable is a {@link Readable} with a `dispose` method that removes all subscribers and locks the Readable.
 *
 * @category Readable
 */
interface OwnedReadable<TValue = any> extends Readable<TValue> {
    /**
     * Remove all subscribers and lock.
     */
    dispose(): void;
}
/**
 * A Readable provider is an object that provides a {@link Readable} `$` property.
 *
 * @category Readable
 */
interface ReadableProvider<TValue = any> {
    readonly $: Readable<TValue>;
}
/**
 * A {@link Readable} or a {@link ReadableProvider}.
 *
 * @category Readable
 */
type ReadableLike<TValue = any> = Readable<TValue> | ReadableProvider<TValue>;
type Subscriber<TValue = any> = (newValue: TValue) => void;
type Version = number;

type MapReadablesToValues<TDepValues extends readonly ReadableLike[]> = {
    [K in keyof TDepValues]: TDepValues[K] extends ReadableLike<infer V> ? V : never;
};

interface UseValue {
    /**
     * Accepts a {@link ReadableLike} and returns the latest value.
     * It only triggers re-rendering when new value emitted from $ (base on {@link Readable.version} instead of React's `Object.is` comparison).
     *
     * @param $ A {@link ReadableLike}.
     * @returns the value of the {@link ReadableLike}
     */
    <T = any>($: ReadableLike<T>): T;
    /**
     * Accepts a {@link ReadableLike} and returns the latest value.
     * It only triggers re-rendering when new value emitted from $ (base on {@link Readable.version} instead of React's `Object.is` comparison).
     *
     * @param $ A {@link ReadableLike}.
     * @returns the value of the {@link ReadableLike}, or $ itself if $ is not a {@link ReadableLike}
     */
    <T = any, U = any>($: ReadableLike<T> | U): T | U;
}
/**
 * Accepts a {@link ReadableLike} and returns the latest value.
 * It only triggers re-rendering when new value emitted from $ (base on {@link Readable.version} instead of React's `Object.is` comparison).
 *
 * @category Hooks
 * @param $ A {@link ReadableLike}.
 * @param scheduler - An optional {@link Scheduler} to control the update frequency. If not provided, updates are applied synchronously.
 * @returns the value of the {@link ReadableLike}, or $ itself if $ is not a {@link ReadableLike}
 *
 * @example
 * ```tsx
 * import { useValue } from "@embra/reactivity/react";
 *
 * function App({ count$ }) {
 *   const count = useValue(count$);
 *   return <div>{count}</div>;
 * }
 * ```
 *
 * @example
 * ```tsx
 * import { useValue, MicrotaskScheduler } from "@embra/reactivity/react";
 *
 * function App({ rapidChangeCount$ }) {
 *   const count = useValue(rapidChangeCount$, MicrotaskScheduler); // update at most once per microtask
 *   return <div>{count}</div>;
 * }
 * ```
 */
declare const useValue: UseValue;

interface UseCombine {
    /**
     * Combines an array of {@link ReadableLike}s into a single {@link Readable} with the array of values.
     * @param deps An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
     * @returns A {@link Readable} with the combined values.
     */
    <TDeps extends readonly ReadableLike[]>(deps: TDeps): OwnedReadable<MapReadablesToValues<TDeps>>;
    /**
     * Combines an array of {@link ReadableLike}s into a single {@link Readable} with transformed value.
     * @param deps - An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
     * @param transform - A pure function that takes multiple values and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns An {@link OwnedReadable} with the transformed values.
     */
    <TDeps extends readonly ReadableLike[], TValue>(deps: TDeps, transform: (...deps: MapReadablesToValues<TDeps>) => TValue, config?: Config<TValue>): OwnedReadable<TValue>;
}
/**
 * Combines an array of {@link ReadableLike}s into a single {@link Readable} with transformed value.
 *
 * Note that changes to `transform` and `config` will not trigger re-derivation; `useCombine` always uses the latest `transform` and `config` in the derivation.
 *
 * @category Hooks
 * @param deps - An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
 * @param transform - Optional pure function that takes multiple values and returns a new value.
 * @param config - Optional custom {@link Config}.
 * @returns An {@link OwnedReadable} with the transformed values.
 *
 * @example
 * ```tsx
 * import { useCombine, useValue } from "@embra/reactivity/react";
 *
 * function App({ width$, height$ }) {
 *   const size$ = useCombine(
 *     [width$, height$],
 *     ([width, height]) => ({ width, height }),
 *     { equal: (s1, 22) => s1.width === s2.width && s1.height === s2.height }
 *   );
 *   const size = useValue(size$);
 * }
 * ```
 */
declare const useCombine: UseCombine;

interface UseCombined {
    /**
     * Combines an array of {@link ReadableLike}s into an array of values.
     * @param deps An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
     * @returns The combined values.
     */
    <TDeps extends readonly ReadableLike[]>(deps: TDeps): MapReadablesToValues<TDeps>;
    /**
     * Combines an array of {@link ReadableLike}s into a transformed value.
     * @param deps - An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
     * @param transform - A pure function that takes multiple values and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns The transformed values.
     */
    <TDeps extends readonly ReadableLike[], TValue>(deps: TDeps, transform: (...deps: MapReadablesToValues<TDeps>) => TValue, config?: Config<TValue>): TValue;
}
/**
 * Combines an array of {@link ReadableLike}s into a transformed value.
 *
 * Note that changes to `transform` and `config` will not trigger re-derivation, and `useCombine` always uses the latest `transform` and `config` in the derivation.
 *
 * @category Hooks
 * @param deps - An array of {@link ReadableLike}s to combine. It follows the React rules of hooks where the length of the array must remain constant.
 * @param transform - Optional pure function that takes multiple values and returns a new value.
 * @param config - Optional custom {@link Config}.
 * @returns The transformed values.
 *
 * @example
 * ```tsx
 * import { useCombined } from "@embra/reactivity/react";
 *
 * function App({ width$, height$ }) {
 *   const size = useCombined(
 *     [width$, height$],
 *     ([width, height]) => ({ width, height }),
 *     { equal: (s1, 22) => s1.width === s2.width && s1.height === s2.height }
 *   );
 * }
 * ```
 */
declare const useCombined: UseCombined;

interface UseDerive {
    /**
     * Derive a new {@link Readable} with same value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from.
     * @returns A {@link Readable} with same value as the given {@link ReadableLike}.
     */
    <TDepValue, TValue>(dep: ReadableLike<TDepValue>): OwnedReadable<TValue>;
    /**
     * Derive a new {@link Readable} with same value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from, or a non-Readable value that will be returned as-is.
     * @returns A {@link Readable} with same value as the given {@link ReadableLike}, or `dep` itself if `dep` is not a {@link ReadableLike}.
     */
    <TDepValue, TValue, U>(dep: ReadableLike<TDepValue> | U): OwnedReadable<TValue> | U;
    /**
     * Derive a new {@link Readable} with transformed value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from.
     * @param transform - A pure function that takes an input value and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns A {@link Readable} with transformed value from the given {@link ReadableLike}.
     */
    <TDepValue, TValue>(dep: ReadableLike<TDepValue>, transform: (depValue: TDepValue) => TValue, config?: Config<TValue>): OwnedReadable<TValue>;
    /**
     * Derive a new {@link Readable} with transformed value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from, or a non-Readable value that will be returned as-is.
     * @param transform - A pure function that takes an input value and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns A {@link Readable} with transformed value from the given {@link ReadableLike}, or `dep` itself if `dep` is not a {@link ReadableLike}.
     */
    <TDepValue, TValue, U>(dep: ReadableLike<TDepValue> | U, transform: (depValue: TDepValue) => TValue, config?: Config<TValue>): OwnedReadable<TValue> | U;
}
/**
 * Derive a new {@link Readable} from the given {@link ReadableLike}.
 *
 * Note that changes to `transform` and `config` will not trigger re-derivation, and `useDerive` always uses the latest `transform` and `config` in the derivation.
 *
 * @category Hooks
 * @param dep - The {@link ReadableLike} to derive from, or a non-Readable value that will be returned as-is.
 * @param transform - Optional pure function that takes an input value and returns a new value.
 * @param config - Optional custom {@link Config}.
 * @returns A {@link Readable} with transformed value from the given {@link ReadableLike}, or `dep` itself if `dep` is not a {@link ReadableLike}.
 *
 * @example
 * ```tsx
 * import { useDerive, useValue } from "@embra/reactivity/react";
 *
 * function App({ position3d$ }) {
 *   const position2d$ = useDerive(
 *     position3d$,
 *     ({ x, y }) => ({ x, y }),
 *     { equal: (p1, p2) => p1.x === p2.x && p1.y === p2.y }
 *   );
 *   const position2d = useValue(position2d$);
 * }
 * ```
 */
declare const useDerive: UseDerive;

interface UseDerived {
    /**
     * Derive a new {@link Readable} with transformed value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from.
     * @param transform - A pure function that takes an input value and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns Transformed value from the given {@link ReadableLike}.
     */
    <TDepValue, TValue>(dep: ReadableLike<TDepValue>, transform: (depValue: TDepValue) => TValue, config?: Config<TValue>): TValue;
    /**
     * Derive a new {@link Readable} with transformed value from the given {@link ReadableLike}.
     * @param dep - The {@link ReadableLike} to derive from, or a non-Readable value that will be returned as-is.
     * @param transform A pure function that takes an input value and returns a new value.
     * @param config - Optional custom {@link Config}.
     * @returns Transformed value from the given {@link ReadableLike}, or `dep` itself if `dep` is not a {@link ReadableLike}.
     */
    <TDepValue, TValue, U>(dep: ReadableLike<TDepValue> | U, transform: (depValue: TDepValue) => TValue, config?: Config<TValue>): TValue | U;
}
/**
 * Derive a new {@link Readable} from the given {@link ReadableLike}.
 *
 * Note that changes to `transform` and `config` will not trigger re-derivation, and `useDerive` always uses the latest `transform` and `config` in the derivation.
 *
 * @category Hooks
 * @param dep - The {@link ReadableLike} to derive from, or a non-Readable value that will be returned as-is.
 * @param transform Optional pure function that takes an input value and returns a new value.
 * @param config - Optional custom {@link Config}.
 * @returns Transformed value from the given {@link ReadableLike}, or `dep` itself if `dep` is not a {@link ReadableLike}.
 *
 * @example
 * ```tsx
 * import { useDerived } from "@embra/reactivity/react";
 *
 * function App({ position3d$ }) {
 *   const position2d = useDerived(
 *     position3d$,
 *     ({ x, y }) => ({ x, y }),
 *     { equal: (p1, p2) => p1.x === p2.x && p1.y === p2.y }
 *   );
 * }
 * ```
 */
declare const useDerived: UseDerived;

export { type UseCombine, type UseCombined, type UseDerive, type UseDerived, type UseValue, useCombine, useCombined, useDerive, useDerived, useValue };
