var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { jsx as _jsx } from "react/jsx-runtime";
import * as React from 'react';
import { Navigate, createBrowserRouter, redirect } from 'react-router';
import { resolveReferrer, verifySessionToken } from '../auth';
import { DefaultFallbackStatic } from '../components/fallback/default';
import ErrorStatusCodePage from '../pages/error-status-code-page';
import RouteErrorBoundary from '../pages/route-error-boundary';
import {} from '../types/core';
import DynamicAuthComponent from '../shared/dynamic-component/dynamic-auth-component';
import AuthenticatedRoot, { createAuthenticatedRootLoader } from '../shared/root/authenticated-root';
import UnauthenticatedRoot from '../shared/root/unauthenticated-root';
import RouteContent, { createRouteLoader } from './route-content';
/**
 * Clean a single path segment by removing leading/trailing slashes
 */
function cleanPath(path) {
    return path.replace(/^\/+|\/+$/g, '');
}
/**
 * Helper function to clean and join path segments properly
 */
function joinPaths(parentPath, childPath) {
    // Remove leading/trailing slashes from both parts
    const cleanParent = cleanPath(parentPath);
    const cleanChild = cleanPath(childPath);
    // Join with single slash and ensure leading slash
    if (cleanParent === '' && cleanChild === '') {
        return '/';
    }
    if (cleanParent === '') {
        return `/${cleanChild}`;
    }
    if (cleanChild === '') {
        return `/${cleanParent}`;
    }
    return `/${cleanParent}/${cleanChild}`;
}
export function createRoute(route, snapshot, routeDefMap) {
    // Add to route definition map for quick lookup
    routeDefMap.set(route.id, route);
    const sharedProps = {
        id: route.id,
        caseSensitive: route.case_sensitive,
        hydrateFallbackElement: _jsx(DefaultFallbackStatic, {}),
        ErrorBoundary: RouteErrorBoundary,
    };
    switch (route.__typename) {
        case 'IndexRoute':
            return Object.assign(Object.assign({}, sharedProps), { index: true, element: _jsx(RouteContent, { route: route }, route.id), loader: createRouteLoader(route, snapshot) });
        case 'PageRoute':
            return Object.assign(Object.assign({}, sharedProps), { path: cleanPath(route.path), element: _jsx(RouteContent, { route: route }, route.id), loader: createRouteLoader(route, snapshot), children: route.children.map((r) => createRoute(r, snapshot, routeDefMap)) });
        case 'LayoutRoute':
            return Object.assign(Object.assign({}, sharedProps), { element: _jsx(RouteContent, { route: route }, route.id), loader: createRouteLoader(route, snapshot), children: route.children.map((r) => createRoute(r, snapshot, routeDefMap)) });
        case 'PrefixRoute':
            return Object.assign(Object.assign({}, sharedProps), { path: cleanPath(route.path), children: route.children.map((r) => createRoute(r, snapshot, routeDefMap)) });
        default:
            throw new Error(`Unknown route type ${JSON.stringify(route)}`);
    }
}
/**
 * Find the first navigatable path in the given routes.
 * Walks the routes in a BFS and returns the first route with a path.
 */
export function findFirstPath(routes) {
    const queue = [{ routes, path: '/' }];
    while (queue.length > 0) {
        const { routes: currentRoutes, path: currentPath } = queue.shift();
        // Categorize routes at current level
        let indexRoute = null;
        const pageRoutes = [];
        const otherRoutes = [];
        for (const route of currentRoutes) {
            if (route.__typename === 'IndexRoute') {
                indexRoute = route;
            }
            else if (route.__typename === 'PageRoute') {
                pageRoutes.push(route);
            }
            else if (route.__typename === 'LayoutRoute' || route.__typename === 'PrefixRoute') {
                otherRoutes.push(route);
            }
        }
        // 1. Prefer index routes - return current path as it's navigatable
        if (indexRoute) {
            return currentPath === '' ? '/' : joinPaths('', currentPath);
        }
        // 2. Then prefer page routes - just return first one joined with parent
        if (pageRoutes.length > 0) {
            return joinPaths(currentPath, pageRoutes[0].path);
        }
        // 3. Add layout/prefix routes to queue for processing
        for (const otherRoute of otherRoutes) {
            const routePath = 'path' in otherRoute ? otherRoute.path : '';
            queue.push({ routes: otherRoute.children, path: joinPaths(currentPath, routePath) });
        }
    }
    return '/';
}
/**
 * Simple boolean to track whether we have done an initial verification of the token.
 * We only do it once, as we don't want to do it on every navigation - any server interaction will require a valid token anyway.
 * The initial check is only done to ensure that on initial load we short-circuit and bail out as early as possible.
 */
let verifiedToken = false;
/**
 * Create the router for the application
 */
export function createRouter(config, snapshot) {
    var _a;
    let basename = '/';
    // The base_url is set in the html template by the backend when returning it
    if (window.dara.base_url !== '') {
        basename = new URL(window.dara.base_url, window.origin).pathname;
    }
    const _b = config.auth_components, { login, logout } = _b, extraRoutes = __rest(_b, ["login", "logout"]);
    // Create map to collect route definitions during route creation
    const routeDefMap = new Map();
    const userRoutes = config.router.children.map((r) => createRoute(r, snapshot, routeDefMap));
    // Use default path from config if set, otherwise fallback to first navigatable path
    const defaultPath = (_a = config.router.default_path) !== null && _a !== void 0 ? _a : (findFirstPath(config.router.children) || '/');
    const router = createBrowserRouter([
        {
            // wrapper around all the router content
            element: _jsx(UnauthenticatedRoot, {}),
            hydrateFallbackElement: _jsx(DefaultFallbackStatic, {}),
            ErrorBoundary: RouteErrorBoundary,
            children: [
                {
                    path: 'error',
                    Component: ErrorStatusCodePage,
                },
                // core auth routes
                {
                    path: 'login',
                    element: _jsx(DynamicAuthComponent, { component: login }),
                },
                {
                    path: 'logout',
                    element: _jsx(DynamicAuthComponent, { component: logout }),
                },
                // extra auth routes
                ...Object.entries(extraRoutes).map(([path, component]) => ({
                    path,
                    element: _jsx(DynamicAuthComponent, { component: component }),
                })),
                // root of the app
                {
                    element: _jsx(AuthenticatedRoot, { daraData: config }),
                    loader: createAuthenticatedRootLoader(config),
                    // token must be set to access the authenticated routes
                    middleware: [
                        () => __awaiter(this, void 0, void 0, function* () {
                            var _a, _b;
                            if (verifiedToken) {
                                return;
                            }
                            // if we can verify the current cookie/bearer session, we're good to go
                            if (yield verifySessionToken()) {
                                verifiedToken = true;
                                return;
                            }
                            // otherwise there is no valid session, redirect to login
                            const referrer = resolveReferrer();
                            const baseUrl = (_b = (_a = window.dara) === null || _a === void 0 ? void 0 : _a.base_url) !== null && _b !== void 0 ? _b : '';
                            const redirectUrl = new URL(`${baseUrl}/login`, window.location.origin);
                            redirectUrl.searchParams.set('referrer', referrer);
                            // Intended RR API usage
                            // eslint-disable-next-line @typescript-eslint/only-throw-error
                            throw redirect(redirectUrl.toString());
                        }),
                    ],
                    // user-defined routes
                    children: [
                        ...userRoutes,
                        // fallback route, redirect to first navigatable path
                        {
                            path: '*',
                            element: _jsx(Navigate, { to: defaultPath }),
                        },
                    ],
                },
            ],
        },
    ], { basename });
    return {
        router,
        routeDefinitions: config.router.children,
        routeObjects: userRoutes,
        routeDefMap,
        defaultPath,
        routeMatches: config.router.route_matches,
    };
}
//# sourceMappingURL=create-router.js.map