import { createContext, useContext, splitProps, mergeProps, createSignal, createComputed, } from 'solid-js';
import { createStore as createState, } from 'solid-js/store';
import { Show } from 'solid-js/web';
/**
 * Default Loader when the store is computing the initial state
 */
const DefaultLoader = () => <p>Loading...</p>;
const generateStore = async ({ state, actions, effects, props }) => {
    const finalStore = await state(props);
    const [get, set] = createState(finalStore);
    const finalActions = actions ? actions(set, get, props) : {};
    if (effects) {
        for (const effect of effects(set, get, props)) {
            createComputed(effect);
        }
    }
    return [get, { ...finalActions, set }];
};
/**
 * @param options - A function An object describing your store
 * @returns [Provider, useProvider] - A tuple Provider/useProvider
 *
 * @example
 * ```tsx
 * const [Provider, useProvider] = createStore({
 *  state: () => ({ count: 0 }),
 *
 *  actions: (set) => ({
 *    increment = (by = 1) => set('count', c => c + by)
 *  })
 * })
 *
 * const App = () => {
 *  const [store, { increment }] = useStore()
 *
 *  return <button onClick={[increment, 1]}>{store.count}</button>
 * }
 *
 * const app = createApp(App)
 * app.use(Provider)
 * app.mount('#app')
 * ```
 */
export function createStore({ state, actions, props, effects, }) {
    const Context = createContext();
    const Provider = (providerProps) => {
        const finalProps = mergeProps(props || {}, providerProps);
        const [internal, external] = splitProps(finalProps, ['children']);
        const [value, setValue] = createSignal();
        generateStore({
            state,
            actions,
            effects,
            props: external,
        }).then((value) => setValue(value));
        return (
        // FIXME: This <Show> will break async SSR
        <Show when={!!value()} fallback={finalProps.loader || DefaultLoader}>
        <Context.Provider value={value()}>{internal.children}</Context.Provider>
      </Show>);
    };
    return [Provider, () => useContext(Context)];
}
