"use strict";
'use client';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.store = void 0;
exports.useStore = useStore;
exports.useRouteInfo = useRouteInfo;
const native_1 = require("@react-navigation/native");
const expo_constants_1 = __importDefault(require("expo-constants"));
const react_1 = require("react");
const react_native_1 = require("react-native");
const extractPathFromURL_1 = require("../fork/extractPathFromURL");
const getStateFromPath_forks_1 = require("../fork/getStateFromPath-forks");
const getLinkingConfig_1 = require("../getLinkingConfig");
const getReactNavigationConfig_1 = require("../getReactNavigationConfig");
const getRoutes_1 = require("../getRoutes");
const routeInfo_1 = require("./routeInfo");
const href_1 = require("../link/href");
const useScreens_1 = require("../useScreens");
const PreviewRouteContext_1 = require("../link/preview/PreviewRouteContext");
const url_1 = require("../utils/url");
const SplashScreen = __importStar(require("../views/Splash"));
const storeRef = {
    current: {},
};
const routeInfoCache = new WeakMap();
const routeInfoValuesCache = new Map();
let splashScreenAnimationFrame;
let hasAttemptedToHideSplash = false;
exports.store = {
    shouldShowTutorial() {
        return !storeRef.current.routeNode && process.env.NODE_ENV === 'development';
    },
    get state() {
        return storeRef.current.state;
    },
    get navigationRef() {
        return storeRef.current.navigationRef;
    },
    get routeNode() {
        return storeRef.current.routeNode;
    },
    getRouteInfo() {
        return storeRef.current.routeInfo || routeInfo_1.defaultRouteInfo;
    },
    get redirects() {
        return storeRef.current.redirects || [];
    },
    get rootComponent() {
        return storeRef.current.rootComponent;
    },
    getStateForHref(href, options) {
        href = (0, href_1.resolveHref)(href);
        href = (0, href_1.resolveHrefStringWithSegments)(href, exports.store.getRouteInfo(), options);
        return this.linking?.getStateFromPath(href, this.linking.config);
    },
    get linking() {
        return storeRef.current.linking;
    },
    setFocusedState(state) {
        const routeInfo = getCachedRouteInfo(state);
        storeRef.current.routeInfo = routeInfo;
    },
    onReady() {
        if (!hasAttemptedToHideSplash) {
            hasAttemptedToHideSplash = true;
            // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.
            splashScreenAnimationFrame = requestAnimationFrame(() => {
                SplashScreen._internal_maybeHideAsync?.();
            });
        }
    },
    onStateChange(newState) {
        if (!newState) {
            return;
        }
        if (process.env.NODE_ENV === 'development') {
            let isStale = false;
            let state = newState;
            while (!isStale && state) {
                isStale = state.stale;
                state =
                    state.routes?.['index' in state && typeof state.index === 'number'
                        ? state.index
                        : state.routes.length - 1]?.state;
            }
            if (isStale) {
                // This should never happen, as onStateChange should provide a full state. However, adding this check to catch any undocumented behavior.
                console.error('Detected stale state in onStateChange. This is likely a bug in Expo Router.');
            }
        }
        storeRef.current.state = newState;
        storeRef.current.routeInfo = getCachedRouteInfo(newState);
        for (const callback of routeInfoSubscribers) {
            callback();
        }
    },
    assertIsReady() {
        if (!storeRef.current.navigationRef.isReady()) {
            throw new Error('Attempted to navigate before mounting the Root Layout component. Ensure the Root Layout component is rendering a Slot, or other navigator on the first render.');
        }
    },
};
function useStore(context, linkingConfigOptions, serverUrl) {
    const navigationRef = (0, native_1.useNavigationContainerRef)();
    const config = expo_constants_1.default.expoConfig?.extra?.router;
    let linking;
    let rootComponent = react_1.Fragment;
    let initialState;
    const routeNode = (0, getRoutes_1.getRoutes)(context, {
        ...config,
        skipGenerated: true,
        ignoreEntryPoints: true,
        platform: react_native_1.Platform.OS,
        preserveRedirectAndRewrites: true,
    });
    const redirects = [config?.redirects, config?.rewrites]
        .filter(Boolean)
        .flat()
        .map((route) => {
        return [
            (0, getStateFromPath_forks_1.routePatternToRegex)((0, getReactNavigationConfig_1.parseRouteSegments)(route.source)),
            route,
            (0, url_1.shouldLinkExternally)(route.destination),
        ];
    });
    if (routeNode) {
        // We have routes, so get the linking config and the root component
        linking = (0, getLinkingConfig_1.getLinkingConfig)(routeNode, context, () => exports.store.getRouteInfo(), {
            metaOnly: linkingConfigOptions.metaOnly,
            serverUrl,
            redirects,
            skipGenerated: config?.skipGenerated ?? false,
            sitemap: config?.sitemap ?? true,
            notFound: config?.notFound ?? true,
        });
        rootComponent = (0, useScreens_1.getQualifiedRouteComponent)(routeNode);
        // By default React Navigation is async and does not render anything in the first pass as it waits for `getInitialURL`
        // This will cause static rendering to fail, which once performs a single pass.
        // If the initialURL is a string, we can prefetch the state and routeInfo, skipping React Navigation's async behavior.
        const initialURL = linking?.getInitialURL?.();
        if (typeof initialURL === 'string') {
            let initialPath = (0, extractPathFromURL_1.extractExpoPathFromURL)(linking.prefixes, initialURL);
            // It does not matter if the path starts with a `/` or not, but this keeps the behavior consistent
            if (!initialPath.startsWith('/'))
                initialPath = '/' + initialPath;
            initialState = linking.getStateFromPath(initialPath, linking.config);
            const initialRouteInfo = (0, routeInfo_1.getRouteInfoFromState)(initialState);
            routeInfoCache.set(initialState, initialRouteInfo);
            routeInfoValuesCache.set(JSON.stringify(initialRouteInfo), initialRouteInfo);
        }
    }
    else {
        // Only error in production, in development we will show the onboarding screen
        if (process.env.NODE_ENV === 'production') {
            throw new Error('No routes found');
        }
        // In development, we will show the onboarding screen
        rootComponent = react_1.Fragment;
    }
    if (react_native_1.Platform.OS === 'android' && storeRef.current.state && storeRef.current.context === context) {
        initialState = storeRef.current.state;
    }
    storeRef.current = {
        navigationRef,
        routeNode,
        config,
        rootComponent,
        linking,
        redirects,
        state: initialState,
        context,
    };
    if (initialState) {
        storeRef.current.routeInfo = getCachedRouteInfo(initialState);
    }
    (0, react_1.useEffect)(() => {
        return () => {
            // listener();
            if (splashScreenAnimationFrame) {
                cancelAnimationFrame(splashScreenAnimationFrame);
                splashScreenAnimationFrame = undefined;
            }
        };
    });
    return exports.store;
}
const routeInfoSubscribers = new Set();
const routeInfoSubscribe = (callback) => {
    routeInfoSubscribers.add(callback);
    return () => {
        routeInfoSubscribers.delete(callback);
    };
};
function useRouteInfo() {
    const routeInfo = (0, react_1.useSyncExternalStore)(routeInfoSubscribe, exports.store.getRouteInfo, exports.store.getRouteInfo);
    const { isPreview, segments, params, pathname } = (0, PreviewRouteContext_1.usePreviewInfo)();
    if (isPreview) {
        return {
            pathname: pathname ?? '',
            segments: segments ?? [],
            unstable_globalHref: '',
            params: params ?? {},
            searchParams: new URLSearchParams(),
            pathnameWithParams: pathname ?? '',
            isIndex: false,
        };
    }
    return routeInfo;
}
function getCachedRouteInfo(state) {
    let routeInfo = routeInfoCache.get(state);
    if (!routeInfo) {
        routeInfo = (0, routeInfo_1.getRouteInfoFromState)(state);
        const routeInfoString = JSON.stringify(routeInfo);
        // Using cached values to avoid re-renders, to increase the chance that the object reference is the same
        const cachedRouteInfo = routeInfoValuesCache.get(routeInfoString);
        if (cachedRouteInfo) {
            routeInfo = cachedRouteInfo;
        }
        else {
            routeInfoValuesCache.set(routeInfoString, routeInfo);
        }
        routeInfoCache.set(state, routeInfo);
    }
    return routeInfo;
}
//# sourceMappingURL=router-store.js.map