import * as Solid from 'solid-js';
import { useRouter } from './useRouter';
function _resolveBlockerOpts(opts, condition) {
    if (opts === undefined) {
        return {
            shouldBlockFn: () => true,
            withResolver: false,
        };
    }
    if ('shouldBlockFn' in opts) {
        return opts;
    }
    if (typeof opts === 'function') {
        const shouldBlock = Boolean(condition ?? true);
        const _customBlockerFn = async () => {
            if (shouldBlock)
                return await opts();
            return false;
        };
        return {
            shouldBlockFn: _customBlockerFn,
            enableBeforeUnload: shouldBlock,
            withResolver: false,
        };
    }
    const shouldBlock = Solid.createMemo(() => Boolean(opts.condition ?? true));
    const _customBlockerFn = async () => {
        if (shouldBlock() && opts.blockerFn !== undefined) {
            return await opts.blockerFn();
        }
        return shouldBlock();
    };
    return {
        get shouldBlockFn() {
            return _customBlockerFn;
        },
        get enableBeforeUnload() {
            return shouldBlock();
        },
        get withResolver() {
            return opts.blockerFn === undefined;
        },
    };
}
export function useBlocker(opts, condition) {
    const props = Solid.merge({
        enableBeforeUnload: true,
        disabled: false,
        withResolver: false,
    }, _resolveBlockerOpts(opts, condition));
    const router = useRouter();
    const [resolver, setResolver] = Solid.createSignal({
        status: 'idle',
        current: undefined,
        next: undefined,
        action: undefined,
        proceed: undefined,
        reset: undefined,
    });
    let disposeBlock;
    Solid.createEffect(() => props.disabled, (disabled) => {
        // Dispose previous blocker registration when re-running
        disposeBlock?.();
        disposeBlock = undefined;
        if (disabled) {
            return;
        }
        const blockerFnComposed = async (blockerFnArgs) => {
            function getLocation(location) {
                const parsedLocation = router.parseLocation(location);
                const matchedRoutes = router.getMatchedRoutes(parsedLocation.pathname);
                if (matchedRoutes.foundRoute === undefined) {
                    return {
                        routeId: '__notFound__',
                        fullPath: parsedLocation.pathname,
                        pathname: parsedLocation.pathname,
                        params: matchedRoutes.routeParams,
                        search: parsedLocation.search,
                    };
                }
                return {
                    routeId: matchedRoutes.foundRoute.id,
                    fullPath: matchedRoutes.foundRoute.fullPath,
                    pathname: parsedLocation.pathname,
                    params: matchedRoutes.routeParams,
                    search: parsedLocation.search,
                };
            }
            const current = getLocation(blockerFnArgs.currentLocation);
            const next = getLocation(blockerFnArgs.nextLocation);
            if (current.routeId === '__notFound__' &&
                next.routeId !== '__notFound__') {
                return false;
            }
            const shouldBlock = await props.shouldBlockFn({
                action: blockerFnArgs.action,
                current,
                next,
            });
            if (!props.withResolver) {
                return shouldBlock;
            }
            if (!shouldBlock) {
                return false;
            }
            const promise = new Promise((resolve) => {
                setResolver({
                    status: 'blocked',
                    current,
                    next,
                    action: blockerFnArgs.action,
                    proceed: () => resolve(false),
                    reset: () => resolve(true),
                });
            });
            const canNavigateAsync = await promise;
            setResolver({
                status: 'idle',
                current: undefined,
                next: undefined,
                action: undefined,
                proceed: undefined,
                reset: undefined,
            });
            return canNavigateAsync;
        };
        disposeBlock = router.history.block({
            blockerFn: blockerFnComposed,
            enableBeforeUnload: props.enableBeforeUnload,
        });
    });
    // Clean up on unmount
    Solid.onSettled(() => {
        return () => {
            disposeBlock?.();
            disposeBlock = undefined;
        };
    });
    return resolver;
}
const _resolvePromptBlockerArgs = (props) => {
    if ('shouldBlockFn' in props) {
        return props;
    }
    const shouldBlock = Solid.createMemo(() => Boolean(props.condition ?? true));
    const _customBlockerFn = async () => {
        if (shouldBlock() && props.blockerFn !== undefined) {
            return await props.blockerFn();
        }
        return shouldBlock;
    };
    return {
        shouldBlockFn: _customBlockerFn,
        get enableBeforeUnload() {
            return shouldBlock();
        },
        get withResolver() {
            return props.blockerFn === undefined;
        },
    };
};
export const Block = function Block(opts) {
    const propsWithChildren = {
        get children() {
            return opts.children;
        },
    };
    const args = _resolvePromptBlockerArgs(opts);
    const resolver = useBlocker(args);
    const children = Solid.createMemo(() => {
        const child = propsWithChildren.children;
        if (resolver && typeof child === 'function')
            return child(resolver());
        return child;
    });
    return <>{children()}</>;
};
//# sourceMappingURL=useBlocker.jsx.map