import {
	createRoute as createRouteTanstack,
	createRouter,
	createHashHistory,
	RouterProvider,
	ErrorComponent,
	Navigate,
	createRootRouteWithContext,
} from '@tanstack/react-router';

// Layout component.
import SidebarLayout from '@AdminComponents/layout/sidebar-layout';
import { SureRankLogo } from '@/global/components/icons';
import currentUserCan from './role-capabilities';

/**
 * @typedef {Object} RouteConfig
 * @property {string}                        path             - The path of the route
 * @property {import('react').ComponentType} component        - The component to render for this route
 * @property {Function}                      [loader]         - Optional data loader function for the route
 * @property {Function}                      [action]         - Optional action function for the route
 * @property {import('react').ComponentType} [errorComponent] - Optional error boundary component
 * @property {RouteConfig[]}                 [children]       - Optional child routes
 * @property {Object}                        [meta]           - Optional metadata for the route
 * @property {import('react').ComponentType} [layout]         - Optional custom layout component for this route
 * @property {Object}                        [layoutProps]    - Optional props to pass to the layout component
 */

/**
 * @typedef {Object} NavLink
 * @property {string}                        path       - The path of the link
 * @property {string}                        label      - The label text to display
 * @property {import('react').ComponentType} [icon]     - Optional icon component
 * @property {NavLink[]}                     [children] - Optional child navigation links
 */

/**
 * @typedef {Object} LayoutConfig
 * @property {import('react').ComponentType} component          - The layout component to use
 * @property {Object}                        [props]            - Props to pass to the layout component
 * @property {NavLink[]}                     [props.navLinks]   - Navigation links for the layout
 * @property {boolean}                       [props.navbarOnly] - Whether to show only the navbar
 */

/**
 * Loading spinner component for pending states
 * @return {JSX.Element} Loading spinner component
 */
const LoadingSpinner = () => (
	<div className="flex items-center justify-center p-4">
		<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-brand-800" />
	</div>
);

/**
 * Creates nested routes recursively
 * @param {RouteConfig} routeConfig   - The route configuration
 * @param {Object}      parentRoute   - The parent route object
 * @param {string}      completePath  - The complete path of the route
 * @param {Object}      defaultLayout - The default layout configuration
 * @return {Object} The created route
 */
const createNestedRoutes = (
	routeConfig,
	parentRoute,
	completePath = '',
	defaultLayout
) => {
	const RouteComponent = routeConfig.layout
		? () => {
				const Layout = routeConfig.layout;
				const Component = routeConfig.component;
				const layoutProps = {
					...defaultLayout.props,
					...routeConfig.layoutProps,
				};

				return (
					<Layout { ...layoutProps }>
						<Component />
					</Layout>
				);
		  }
		: routeConfig.component;

	const hasChildren = !! routeConfig.children?.length;
	const nestedRoutes = hasChildren ? [ ...routeConfig.children ] : [];

	if ( ! RouteComponent && hasChildren ) {
		completePath += routeConfig.path;
		const hasIndexRoute = nestedRoutes.some(
			( child ) => child.path === '/'
		);

		if ( ! hasIndexRoute ) {
			const firstChild = nestedRoutes[ 0 ];
			const pathToFirstChild = `${ completePath }${ firstChild.path }`;
			nestedRoutes.push( {
				path: '/',
				component: () => <Navigate to={ pathToFirstChild } replace />,
			} );
		}
	}

	let route;
	const loader = routeConfig.loader ? () => routeConfig.loader : () => void 0;
	if ( routeConfig?.component && 'loader' in routeConfig?.component ) {
		route = createRouteTanstack( {
			getParentRoute: () => parentRoute,
			path: routeConfig.path,
			errorComponent: routeConfig.errorComponent,
			pendingComponent: () => <PendingComponent />,
			loader,
		} ).lazy( routeConfig?.component?.loader );
	} else {
		route = createRouteTanstack( {
			getParentRoute: () => parentRoute,
			path: routeConfig.path,
			component: RouteComponent,
			errorComponent: routeConfig.errorComponent,
			loader,
			action: routeConfig.action,
			meta: routeConfig.meta,
		} );
	}

	if ( hasChildren ) {
		const childRoutes = nestedRoutes.map( ( childConfig ) =>
			createNestedRoutes(
				childConfig,
				route,
				completePath,
				defaultLayout
			)
		);
		route.addChildren( childRoutes );
	}

	return route;
};

/**
 * Creates router configuration
 * @param {Object} routeTree - The route tree
 * @return {Object} Router configuration
 */
const createRouterConfig = ( routeTree ) => ( {
	routeTree,
	history: createHashHistory(),
	defaultPendingComponent: LoadingSpinner,
	defaultPendingMs: 1000,
	defaultErrorComponent: ErrorComponent,
} );

/**
 * Creates an admin router with enhanced features.
 *
 * @param {Object}                        options                         - The options for creating the admin router
 * @param {RouteConfig[]}                 options.routes                  - The routes configuration
 * @param {NavLink[]}                     [options.navLinks]              - Optional navigation links
 * @param {import('react').ComponentType} [options.notFoundComponent]     - Optional 404 page component
 * @param {import('react').ComponentType} [options.defaultErrorComponent] - Optional default error boundary component
 * @param {LayoutConfig}                  [options.defaultLayout]         - Optional default layout configuration
 * @return {Function|null} The router component or null if no routes
 */
const createAdminRouter = ( {
	routes = [],
	navLinks = [],
	notFoundComponent,
	defaultErrorComponent,
	defaultLayout = {
		component: SidebarLayout,
		props: {},
	},
} ) => {
	if ( ! routes?.length ) {
		return null;
	}

	// Create Root route with error boundary
	const rootRoute = createRootRouteWithContext()( {
		component: () => {
			const Layout = defaultLayout.component;
			return (
				<Layout
					routes={ routes }
					navLinks={ navLinks }
					{ ...defaultLayout.props }
				/>
			);
		},
		errorComponent: defaultErrorComponent,
		notFoundComponent,
		loader: () => void 0,
	} );

	// Create routes with nested structure
	const createdRoutes = routes.map( ( route ) =>
		createNestedRoutes( route, rootRoute, '', defaultLayout )
	);

	// Add routes to the root route
	const routeTree = rootRoute.addChildren( createdRoutes );

	// Create router with configuration
	const router = createRouter( createRouterConfig( routeTree ) );

	// Router State provider component
	return () => <RouterProvider router={ router } />;
};

/**
 * Creates a route configuration object
 * @param {string}    path      - Route path
 * @param {Component} component - Route component
 * @param {Array}     children  - Optional child routes
 * @param {Object}    options   - Optional options
 * @return {Object|undefined} Route configuration object
 */
export const createRoute = (
	path,
	component,
	children = null,
	options = {}
) => {
	// Handle - if capability is not allowed, return undefined
	if (
		options?.capability &&
		! currentUserCan( options?.capability )
	) {
		return;
	}
	// Handle - if third param is array, treat as children example : Advanced Settings
	if ( Array.isArray( children ) ) {
		return {
			path,
			component,
			children,
			...options,
		};
	}

	// Handle new options format
	return {
		path,
		component,
		children,
		...options,
	};
};

/**
 * Creates a child route configuration object
 * @param {string}    path      - Route path
 * @param {Component} component - Route component
 * @param {Array}     children  - Optional nested child routes
 * @param {Object}    options   - Optional options
 * @return {Object|undefined} Child route configuration object
 */
export const createChildRoute = ( path, component, children = null, options = {} ) => {
	// Handle - if capability is not allowed, return undefined
	if (
		options?.capability &&
		! currentUserCan( options?.capability )
	) {
		return;
	}
	// Handle - if third param is array, treat as children
	if ( Array.isArray( children ) ) {
		return {
			path,
			component,
			children,
			...options,
		};
	}

	// Handle new options format
	return {
		path,
		component,
		children,
		...options,
	};
};

/**
 * Recursively filters out falsy values from routes array and children
 * @param {RouteConfig[]} routes - Array of route configurations
 * @return {RouteConfig[]} Filtered routes array without falsy values
 */
export const filterFalsyRoutes = ( routes ) => {
	if ( ! Array.isArray( routes ) ) {
		return [];
	}

	return routes
		.filter( ( route ) => !! route ) // Filter out falsy values
		.map( ( route ) => {
			// If route has children, recursively filter them
			if ( route.children?.length ) {
				return {
					...route,
					children: filterFalsyRoutes( route.children ),
				};
			}
			return route;
		} );
};

/**
 * Builds a Set of all available route paths from the routes array
 * @param {RouteConfig[]} routes - Array of route configurations
 * @return {Set<string>} Set of all route paths
 */
export const buildRoutePathMap = ( routes ) => {
	const paths = new Set();

	const extractPaths = ( routeArray, basePath = '' ) => {
		routeArray.forEach( ( route ) => {
			if ( ! route ) {
				return;
			}

			const fullPath = basePath + route.path;
			paths.add( fullPath );

			// Recursively extract child route paths
			if ( route.children?.length ) {
				extractPaths( route.children, fullPath );
			}
		} );
	};

	extractPaths( routes );
	return paths;
};

/**
 * Filters navigation links to only include links that have corresponding routes
 * @param {Array}       navLinks   - Array of navigation link sections
 * @param {Set<string>} routePaths - Set of available route paths
 * @return {Array} Filtered navigation links
 */
export const filterNavLinksByRoutes = ( navLinks, routePaths ) => {
	const filteredSections = [];

	navLinks.forEach( ( section ) => {
		if ( ! section.links ) {
			return;
		}

		const filteredLinks = [];

		section.links.forEach( ( link ) => {
			// Check if the link's path exists in available routes
			if ( routePaths.has( link.path ) ) {
				const filteredLink = { ...link };

				// Recursively filter submenu items
				if ( link.submenu?.length ) {
					const filteredSubmenu = link.submenu.filter( ( submenuItem ) =>
						routePaths.has( submenuItem.path )
					);

					// Only include the link if it has submenu items after filtering
					if ( filteredSubmenu.length > 0 ) {
						filteredLink.submenu = filteredSubmenu;
						filteredLinks.push( filteredLink );
					}
				} else {
					filteredLinks.push( filteredLink );
				}
			}
		} );

		// Only include the section if it has links after filtering
		if ( filteredLinks.length > 0 ) {
			filteredSections.push( {
				...section,
				links: filteredLinks,
			} );
		}
	} );

	return filteredSections;
};

export default createAdminRouter;

const PendingComponent = () => {
	return (
		<div className="w-full h-full flex items-center justify-center p-4">
			<div className="h-96 flex-grow flex items-center justify-center w-full">
				<SureRankLogo className="w-10 h-10 animate-ping" />
			</div>
		</div>
	);
};
