import * as Solid from 'solid-js';
import { replaceEqualDeep, rootRouteId } from '@tanstack/router-core';
import { CatchBoundary, ErrorComponent } from './CatchBoundary';
import { useRouter } from './useRouter';
import { Rendered, Transitioner } from './Transitioner';
import { nearestMatchContext } from './matchContext';
import { SafeFragment } from './SafeFragment';
import { Match } from './Match';
import { renderInNonRouteComponentContext } from './nonRouteComponentContext';
const NearestMatchContext = nearestMatchContext;
/**
 * Select the global loading boundary for `Matches`.
 *
 * Loading UI belongs to the application, not the router. Solid 2's async
 * model never injects fallbacks the app didn't write: pending state
 * propagates through the graph to whatever boundary the app placed (or is
 * held at the root when there is none), and an unresolved `lazy()` chunk is
 * just a pending async value. The wrapper here is therefore strictly opt-in:
 * it renders only when the app configured root pending UI
 * (`pendingComponent` on the root route or `defaultPendingComponent`).
 * Nothing configured means no wrapper — pending match/chunk states propagate
 * as ordinary Solid async.
 *
 * The decision is deliberately SYMMETRIC between server and client, and
 * consults no hydration or environment state. Solid's hydration claims
 * server nodes positionally through the boundary structure, so the client
 * must render a boundary exactly where the server rendered one — a
 * client-only wrapper (even a settled one that renders its children
 * directly) desyncs node claiming and detaches the app from the server DOM.
 * The inputs below (`pendingComponent` configuration,
 * `disableGlobalCatchBoundary`, `router.ssr`) all resolve identically on the
 * server and on the hydrating client, so the trees always agree. This also
 * keeps the decision stable across the app's lifetime: it is made once per
 * `Matches` instance, so gating it on transient state (like "currently
 * hydrating") would permanently freeze the configured pending UI off for
 * every post-hydration navigation of a hydrated app.
 *
 * `router.ssr` (TanStack's `$_TSR` SSR utilities) is symmetric too:
 * `attachRouterServerSsrUtils` sets it on the server before rendering, and
 * router-core `hydrate()` (invoked by `RouterClient`, which TanStack Start
 * builds on) sets it on the client before rendering. It short-circuits the
 * wrapper exactly as before: that protocol legitimately hydrates matches in
 * pending states (`ssr: 'data-only'`), where a settled boundary is not
 * guaranteed.
 *
 * When disableGlobalCatchBoundary is true, we must NOT wrap with
 * Solid.Loading because Solid.Loading transforms STATUS_ERROR into
 * STATUS_PENDING, which prevents errors from propagating to an external
 * Errored boundary.
 */
export function _resolveMatchesLoadingBoundary(router) {
    const pendingComponent = router.routesById[rootRouteId]?.options
        .pendingComponent ?? router.options.defaultPendingComponent;
    return !pendingComponent ||
        router.options.disableGlobalCatchBoundary ||
        router.ssr
        ? SafeFragment
        : Solid.Loading;
}
export function Matches() {
    const router = useRouter();
    const ResolvedSuspense = _resolveMatchesLoadingBoundary(router);
    const rootRoute = () => router.routesById[rootRouteId];
    const PendingComponent = rootRoute().options.pendingComponent ??
        router.options.defaultPendingComponent;
    const OptionalWrapper = router.options.InnerWrap || SafeFragment;
    return (<OptionalWrapper>
      <ResolvedSuspense fallback={PendingComponent
            ? (() => {
                if (process.env.NODE_ENV !== 'production') {
                    return renderInNonRouteComponentContext(() => <PendingComponent />, 'pendingComponent');
                }
                return <PendingComponent />;
            })()
            : null}>
        <Transitioner />
        <MatchesInner />
        <Rendered />
      </ResolvedSuspense>
    </OptionalWrapper>);
}
function MatchesInner() {
    const router = useRouter();
    const routeId = () => router.stores.ids.get()[0];
    const match = () => routeId() ? router.stores.byRoute.get(routeId())?.get() : undefined;
    const nearestMatch = [routeId, match];
    const matchContent = () => (<Solid.Show when={routeId()} keyed>
      {(currentRouteId) => <Match routeId={currentRouteId}/>}
    </Solid.Show>);
    if (router.options.disableGlobalCatchBoundary) {
        // When disableGlobalCatchBoundary is true, render without any internal
        // error boundary so errors bubble up freely to an external Errored boundary.
        return (<NearestMatchContext value={nearestMatch}>
        {matchContent()}
      </NearestMatchContext>);
    }
    return (<NearestMatchContext value={nearestMatch}>
      <CatchBoundary getResetKey={() => router.stores.matches.get()} render={matchContent} errorComponent={ErrorComponent} onCatch={process.env.NODE_ENV !== 'production'
            ? (error) => {
                console.warn(`Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`);
                console.warn(`Warning: ${error.message || error.toString()}`);
            }
            : undefined}/>
    </NearestMatchContext>);
}
export function useMatchRoute() {
    const router = useRouter();
    return (opts) => {
        return Solid.createMemo(() => {
            const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts;
            router.stores.location.get();
            router.stores.resolvedLocation.get();
            router.stores.status.get();
            return router.matchRoute(rest, {
                pending,
                caseSensitive,
                fuzzy,
                includeSearch,
            });
        });
    };
}
export function MatchRoute(props) {
    const matchRoute = useMatchRoute();
    const params = matchRoute(props);
    const renderedChild = Solid.createMemo(() => {
        const matchedParams = params();
        const child = props.children;
        if (typeof child === 'function') {
            return child(matchedParams);
        }
        return matchedParams ? child : null;
    });
    return <>{renderedChild()}</>;
}
export function useMatches(opts) {
    const router = useRouter();
    return Solid.createMemo((prev) => {
        const matches = router.stores.matches.get();
        const res = opts?.select ? opts.select(matches) : matches;
        if (prev === undefined)
            return res;
        return replaceEqualDeep(prev, res);
    });
}
export function useParentMatches(opts) {
    const contextRouteId = Solid.useContext(nearestMatchContext)[0 /* route id */];
    return useMatches({
        select: (matches) => {
            matches = matches.slice(0, matches.findIndex((d) => d.routeId === contextRouteId()));
            return opts?.select ? opts.select(matches) : matches;
        },
    });
}
export function useChildMatches(opts) {
    const contextRouteId = Solid.useContext(nearestMatchContext)[0 /* route id */];
    return useMatches({
        select: (matches) => {
            matches = matches.slice(matches.findIndex((d) => d.routeId === contextRouteId()) + 1);
            return opts?.select ? opts.select(matches) : matches;
        },
    });
}
//# sourceMappingURL=Matches.jsx.map