import * as Vue from 'vue';
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 = Boolean(opts.condition ?? true);
    const fn = opts.blockerFn;
    const _customBlockerFn = async () => {
        if (shouldBlock && fn !== undefined) {
            return await fn();
        }
        return shouldBlock;
    };
    return {
        shouldBlockFn: _customBlockerFn,
        enableBeforeUnload: shouldBlock,
        withResolver: fn === undefined,
    };
}
export function useBlocker(opts, condition) {
    const { shouldBlockFn, enableBeforeUnload = true, disabled = false, withResolver = false, } = _resolveBlockerOpts(opts, condition);
    const router = useRouter();
    const { history } = router;
    const resolver = Vue.ref({
        status: 'idle',
        current: undefined,
        next: undefined,
        action: undefined,
        proceed: undefined,
        reset: undefined,
    });
    Vue.watchEffect((onCleanup) => {
        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);
            // Allow navigation away from 404 pages to valid routes
            if (current.routeId === '__notFound__' &&
                next.routeId !== '__notFound__') {
                return false;
            }
            const shouldBlock = await shouldBlockFn({
                action: blockerFnArgs.action,
                current,
                next,
            });
            if (!withResolver) {
                return shouldBlock;
            }
            if (!shouldBlock) {
                return false;
            }
            const promise = new Promise((resolve) => {
                resolver.value = {
                    status: 'blocked',
                    current,
                    next,
                    action: blockerFnArgs.action,
                    proceed: () => resolve(false),
                    reset: () => resolve(true),
                };
            });
            const canNavigateAsync = await promise;
            resolver.value = {
                status: 'idle',
                current: undefined,
                next: undefined,
                action: undefined,
                proceed: undefined,
                reset: undefined,
            };
            return canNavigateAsync;
        };
        if (disabled) {
            return;
        }
        const unsubscribe = history.block({
            blockerFn: blockerFnComposed,
            enableBeforeUnload,
        });
        onCleanup(() => {
            if (unsubscribe)
                unsubscribe();
        });
    });
    return withResolver ? resolver : undefined;
}
const _resolvePromptBlockerArgs = (props) => {
    if ('shouldBlockFn' in props) {
        return { ...props };
    }
    const shouldBlock = Boolean(props.condition ?? true);
    const fn = props.blockerFn;
    const _customBlockerFn = async () => {
        if (shouldBlock && fn !== undefined) {
            return await fn();
        }
        return shouldBlock;
    };
    return {
        shouldBlockFn: _customBlockerFn,
        enableBeforeUnload: shouldBlock,
        withResolver: fn === undefined,
    };
};
// Internal Block implementation as a proper Vue component for reactivity
const BlockImpl = Vue.defineComponent({
    name: 'Block',
    props: {
        shouldBlockFn: {
            type: Function,
            required: false,
        },
        enableBeforeUnload: {
            type: [Boolean, Function],
            default: true,
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        withResolver: {
            type: Boolean,
            default: false,
        },
        // Legacy props
        blockerFn: {
            type: Function,
            required: false,
        },
        condition: {
            type: [Boolean, Object],
            required: false,
        },
    },
    setup(props, { slots }) {
        // Create a computed that resolves the blocker args reactively
        const blockerArgs = Vue.computed(() => {
            if (props.shouldBlockFn) {
                return {
                    shouldBlockFn: props.shouldBlockFn,
                    enableBeforeUnload: props.enableBeforeUnload,
                    disabled: props.disabled,
                    withResolver: props.withResolver,
                };
            }
            // Legacy handling
            const shouldBlock = Boolean(props.condition ?? true);
            const fn = props.blockerFn;
            const _customBlockerFn = async () => {
                if (shouldBlock && fn !== undefined) {
                    return await fn();
                }
                return shouldBlock;
            };
            return {
                shouldBlockFn: _customBlockerFn,
                enableBeforeUnload: shouldBlock,
                disabled: props.disabled,
                withResolver: fn === undefined,
            };
        });
        // Use a reactive useBlocker that re-subscribes when args change
        const router = useRouter();
        const { history } = router;
        const resolver = Vue.ref({
            status: 'idle',
            current: undefined,
            next: undefined,
            action: undefined,
            proceed: undefined,
            reset: undefined,
        });
        Vue.watchEffect((onCleanup) => {
            const args = blockerArgs.value;
            if (args.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);
                // Allow navigation away from 404 pages to valid routes
                if (current.routeId === '__notFound__' &&
                    next.routeId !== '__notFound__') {
                    return false;
                }
                const shouldBlock = await args.shouldBlockFn({
                    action: blockerFnArgs.action,
                    current,
                    next,
                });
                if (!args.withResolver) {
                    return shouldBlock;
                }
                if (!shouldBlock) {
                    return false;
                }
                const promise = new Promise((resolve) => {
                    resolver.value = {
                        status: 'blocked',
                        current,
                        next,
                        action: blockerFnArgs.action,
                        proceed: () => resolve(false),
                        reset: () => resolve(true),
                    };
                });
                const canNavigateAsync = await promise;
                resolver.value = {
                    status: 'idle',
                    current: undefined,
                    next: undefined,
                    action: undefined,
                    proceed: undefined,
                    reset: undefined,
                };
                return canNavigateAsync;
            };
            const unsubscribe = history.block({
                blockerFn: blockerFnComposed,
                enableBeforeUnload: args.enableBeforeUnload,
            });
            onCleanup(() => {
                if (unsubscribe)
                    unsubscribe();
            });
        });
        return () => {
            const defaultSlot = slots.default;
            if (!defaultSlot) {
                return Vue.h(Vue.Fragment, null);
            }
            // If slot is a function that takes resolver, call it with the resolver
            const slotContent = defaultSlot(resolver.value);
            return Vue.h(Vue.Fragment, null, slotContent);
        };
    },
});
export function Block(opts) {
    const { children, ...rest } = opts;
    // Convert children to slot format for the component
    const slots = children
        ? typeof children === 'function'
            ? { default: children }
            : { default: () => children }
        : undefined;
    return Vue.h(BlockImpl, rest, slots);
}
//# sourceMappingURL=useBlocker.jsx.map