/**
 * @flow strict-local
 * @format
 */

// recipe takes in a virtual object (ie. Proxy) representing the current state,
// tracks all mutation performed on the state behind the scene.
// All mutations are then 'replayed' to construct the new state with as much
// structural sharing as possible
// `state` is the read-only snapshot of the state before the mutation
export type Recipe<T> = (draft: T, state: $ReadOnly<T>) => void;
export type Mutate<T> = (recipe: Recipe<T>) => void;

type ConsumerRender<S> = (...S) => React$Node;

type ProviderProps<T> = {|
  children: React$Node,
  initialState?: T,
|};

export type Provider<T> = React$ComponentType<ProviderProps<T>>;

type GetReturnType = <T, S>((T) => S) => S;

type ConsumerProps<T, TSelect: $ReadOnlyArray<(T) => mixed>> = {|
  // An array of selectors that select a subset of relevant store state
  // that gets passed to the `render` or `children` callback
  select?: TSelect,
  children?: ConsumerRender<$TupleMap<TSelect, GetReturnType>>,
  render?: ConsumerRender<$TupleMap<TSelect, GetReturnType>>,
|};

type Selector<T, R> = T => R;

export type Store<T> = {
  +Provider: Provider<T>,
  +Consumer: {
    <TSelect: $ReadOnlyArray<(T) => mixed>>(
      ConsumerProps<T, TSelect>,
    ): React$Node,
    // Need the following to fake this as a functional component
    displayName?: ?string,
    propTypes?: any,
    contextTypes?: any,
  },
  +mutate: Mutate<T>,
  createSelector<R>(Selector<T, R>): Selector<T, R>,
};

declare export default function createStore<T>(baseState: T): Store<T>;
