import { jsx as _jsx } from "@emotion/react/jsx-runtime";
import React from "react";
import { createContext as createContextSelectorContext, useContext as useContextSelectorContext, useContextSelector, } from "use-context-selector";
import { applyPatches, enablePatches, produce } from "immer";
enablePatches();
export function createContext(initialState, createActions, options = {}) {
    const Context = createContextSelectorContext({
        state: initialState,
        // eslint-disable-next-line @typescript-eslint/no-unused-vars -- for type inference
        setState: (modifier) => { },
        hasUndo: false,
        hasRedo: false,
        undo: () => { },
        redo: () => { },
        clearStack: () => { },
        // eslint-disable-next-line @typescript-eslint/no-unused-vars -- for type inference
        batchActions: (callback) => { },
        isDirty: false,
        clean: () => { },
    });
    const useContext = () => useContextSelectorContext(Context);
    function useSelectState(selector) {
        return useContextSelector(Context, ({ state }) => selector(state));
    }
    const useActions = () => {
        const setState = useContextSelector(Context, ({ setState }) => setState);
        return React.useMemo(() => createActions(setState), [setState]);
    };
    const useHistory = () => useContextSelector(Context, ({ hasUndo, hasRedo, undo, redo, clearStack }) => ({
        hasUndo,
        hasRedo,
        undo,
        redo,
        clear: clearStack,
    }));
    const useBatchActions = () => useContextSelector(Context, ({ batchActions }) => batchActions);
    const useDirty = () => useContextSelector(Context, ({ isDirty, clean }) => ({ isDirty, clean }));
    const Provider = (props) => {
        var _a;
        const [state, setState] = React.useState((_a = props.value) !== null && _a !== void 0 ? _a : initialState);
        const [stack, setStack] = React.useState({
            undos: [],
            redos: [],
            batchIndex: null,
        });
        const [isDirty, setDirty] = React.useState(false);
        const setImmerState = React.useCallback((modifier, track = true) => {
            setState((state) => produce(state, modifier, (patches, inversePatches) => {
                if (patches.length === 0)
                    return;
                if (options.trackPaths &&
                    !options.trackPaths.some((trackPath) => patches.every((patch) => patch.path.join(".").startsWith(trackPath))))
                    return;
                if (track && options.trackChanges) {
                    setDirty(true);
                }
                if (options.enableHistory) {
                    setStack((stack) => {
                        return Object.assign(Object.assign({}, stack), { undos: stack.undos.concat({
                                patches,
                                inversePatches,
                            }), redos: [] });
                    });
                }
            }));
        }, []);
        const hasUndo = stack.undos.length > 0;
        const hasRedo = stack.redos.length > 0;
        const undo = React.useCallback(() => setStack((stack) => {
            const patchGroup = stack.undos.pop();
            if (!patchGroup)
                return stack;
            setState((state) => applyPatches(state, patchGroup.inversePatches));
            return Object.assign(Object.assign({}, stack), { redos: stack.redos.concat(patchGroup) });
        }), []);
        const redo = React.useCallback(() => setStack((stack) => {
            const patchGroup = stack.redos.pop();
            if (!patchGroup)
                return stack;
            setState((state) => applyPatches(state, patchGroup.patches));
            return Object.assign(Object.assign({}, stack), { undos: stack.undos.concat(patchGroup) });
        }), []);
        const batchActions = React.useCallback((callback) => {
            setStack((stack) => (Object.assign(Object.assign({}, stack), { batchIndex: stack.undos.length })));
            callback();
        }, []);
        React.useEffect(() => {
            const { undos, batchIndex } = stack;
            if (batchIndex === null)
                return;
            const batchPatchGroup = undos.slice(batchIndex).reduce((patchGroup, { patches, inversePatches }) => ({
                patches: patchGroup.patches.concat(patches),
                inversePatches: inversePatches.concat(patchGroup.inversePatches),
            }), {
                patches: [],
                inversePatches: [],
            });
            setStack({
                undos: undos.slice(0, batchIndex).concat(batchPatchGroup),
                redos: [],
                batchIndex: null,
            });
        }, [stack.batchIndex]);
        const clearStack = React.useCallback(() => setStack({ undos: [], redos: [], batchIndex: null }), []);
        const clean = React.useCallback(() => setDirty(false), []);
        const value = React.useMemo(() => ({
            state,
            setState: setImmerState,
            hasUndo,
            hasRedo,
            undo,
            redo,
            clearStack,
            batchActions,
            isDirty,
            clean,
        }), [state, setImmerState, stack, hasUndo, hasRedo]);
        return _jsx(Context.Provider, Object.assign({ value: value }, { children: props.children }));
    };
    return {
        useContext,
        useSelectState,
        useActions,
        useHistory,
        useBatchActions,
        useDirty,
        Provider,
    };
}
//# sourceMappingURL=react-immersive-history.js.map