'use client';
import { useState, useCallback, useEffect, useMemo, useRef } from 'react';
function stripBase(pathname, basePath) {
    if (pathname.startsWith(basePath)) {
        const rest = pathname.slice(basePath.length);
        return rest === '' || rest === '/' ? '/' : rest.startsWith('/') ? rest : `/${rest}`;
    }
    return '/';
}
/**
 * Parse the query string out of an admin path (`/pages/new?parent=abc`) or a
 * raw search string (`?parent=abc`) into `URLSearchParams`. Pure helper for
 * view components that only receive `currentPath` / `onNavigate` props.
 */
export function parseRouteQuery(path) {
    return new URLSearchParams(path.split('#')[0].split('?')[1] ?? '');
}
/** Confirmation shown when navigating away from an editor with unsaved changes. */
export const NAVIGATION_CONFIRM_MESSAGE = 'You have unsaved changes that will be lost. Leave this page anyway?';
const navigationBlockers = new Set();
export function registerNavigationBlocker(predicate) {
    navigationBlockers.add(predicate);
    return () => {
        navigationBlockers.delete(predicate);
    };
}
/** True when any registered editor currently has unsaved changes. */
export function hasBlockingNavigation() {
    for (const predicate of navigationBlockers) {
        try {
            if (predicate())
                return true;
        }
        catch {
            // A misbehaving predicate must not wedge navigation.
        }
    }
    return false;
}
/**
 * Register an unsaved-changes guard for an editor. While `isDirty` is true, an
 * in-app navigation prompts for confirmation (via navigate()) and a browser
 * unload/refresh shows the native "leave site?" prompt. The registration and
 * the beforeunload listener are torn down on unmount.
 */
export function useNavigationBlocker(isDirty) {
    const dirtyRef = useRef(isDirty);
    dirtyRef.current = isDirty;
    useEffect(() => registerNavigationBlocker(() => dirtyRef.current), []);
    useEffect(() => {
        if (!isDirty || typeof window === 'undefined')
            return;
        const onBeforeUnload = (e) => {
            e.preventDefault();
            // Required by Chrome/Safari to actually trigger the native prompt.
            e.returnValue = '';
        };
        window.addEventListener('beforeunload', onBeforeUnload);
        return () => window.removeEventListener('beforeunload', onBeforeUnload);
    }, [isDirty]);
}
export function useAdminRouter(basePath = '/admin', serverPath = '/') {
    const [currentPath, setCurrentPath] = useState(serverPath);
    const baseRef = useRef(basePath);
    baseRef.current = basePath;
    useEffect(() => {
        if (typeof window !== 'undefined') {
            // Include the query string so state-bearing URLs (`/pages/new?parent=x`,
            // `/settings?tab=users`) survive hard reloads. matchRoute strips the
            // query before matching, so `/?foo=1` still resolves to `/`.
            const browserPath = stripBase(window.location.pathname, basePath) + window.location.search;
            if (browserPath !== currentPath) {
                setCurrentPath(browserPath);
            }
        }
    }, []); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => {
        function onPopState() {
            // Query string included for the same reason as the mount sync above —
            // back/forward must restore deep-link state, not just the pathname.
            setCurrentPath(stripBase(window.location.pathname, baseRef.current) + window.location.search);
        }
        window.addEventListener('popstate', onPopState);
        return () => window.removeEventListener('popstate', onPopState);
    }, []);
    const navigate = useCallback((path) => {
        const normalizedPath = path.startsWith('/') ? path : `/${path}`;
        // Guard against discarding unsaved editor work on client-side navigation.
        if (hasBlockingNavigation() && typeof window !== 'undefined') {
            if (!window.confirm(NAVIGATION_CONFIRM_MESSAGE))
                return;
        }
        setCurrentPath(normalizedPath);
        const fullUrl = normalizedPath === '/' ? baseRef.current : `${baseRef.current}${normalizedPath}`;
        // `__NA: true` marks this as a native history entry so Next.js 16's patched
        // `history.pushState` short-circuits to the original implementation instead
        // of running its URL-parsing path (which throws "Invalid URL"). The admin is
        // a self-contained SPA with its own `popstate` listener, so we deliberately
        // bypass Next's router/RSC sync on in-admin navigation.
        window.history.pushState({ adminPath: normalizedPath, __NA: true }, '', fullUrl);
    }, []);
    // Build an absolute (basePath-prefixed) admin URL without navigating. Used
    // for links that must load a real document — e.g. opening a preview in a
    // new browser tab, where `pushState` (which only mutates the current tab)
    // is not an option.
    const buildHref = useCallback((path) => {
        const normalizedPath = path.startsWith('/') ? path : `/${path}`;
        return normalizedPath === '/' ? baseRef.current : `${baseRef.current}${normalizedPath}`;
    }, []);
    const matchRoute = useCallback((pattern) => {
        // Match against the path only — query string / hash are state-bearing
        // (e.g. `/seo?tab=audit`) and must not defeat route matching.
        const pathname = currentPath.split('?')[0].split('#')[0];
        const patternParts = pattern.split('/').filter(Boolean);
        const pathParts = pathname.split('/').filter(Boolean);
        if (pattern === '/' && pathParts.length === 0) {
            return {};
        }
        if (patternParts.length !== pathParts.length) {
            return null;
        }
        const params = {};
        for (let i = 0; i < patternParts.length; i++) {
            const patternPart = patternParts[i];
            const pathPart = pathParts[i];
            if (patternPart.startsWith(':')) {
                params[patternPart.slice(1)] = pathPart;
            }
            else if (patternPart !== pathPart) {
                return null;
            }
        }
        return params;
    }, [currentPath]);
    // Raw query string of the current route (`?tab=users`), or '' when absent.
    // Views wanting parsed params can pass it (or currentPath) to parseRouteQuery.
    const search = useMemo(() => {
        const queryIndex = currentPath.indexOf('?');
        return queryIndex === -1 ? '' : currentPath.slice(queryIndex).split('#')[0];
    }, [currentPath]);
    return { currentPath, search, navigate, matchRoute, buildHref };
}
//# sourceMappingURL=index.js.map