/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {
  EnvironmentInjector,
  EnvironmentProviders,
  NgModuleFactory,
  Provider,
  ProviderToken,
  Type,
} from '@angular/core';
import {Observable} from 'rxjs';

import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';

/**
 * How to handle a navigation request to the current URL. One of:
 *
 * - `'ignore'` :  The router ignores the request it is the same as the current state.
 * - `'reload'` : The router processes the URL even if it is not different from the current state.
 * One example of when you might want this option is if a `canMatch` guard depends on
 * application state and initially rejects navigation to a route. After fixing the state, you want
 * to re-navigate to the same URL so the route with the `canMatch` guard can activate.
 *
 * Note that this only configures whether the Route reprocesses the URL and triggers related
 * action and events like redirects, guards, and resolvers. By default, the router re-uses a
 * component instance when it re-navigates to the same component type without visiting a different
 * component first. This behavior is configured by the `RouteReuseStrategy`. In order to reload
 * routed components on same url navigation, you need to set `onSameUrlNavigation` to `'reload'`
 * _and_ provide a `RouteReuseStrategy` which returns `false` for `shouldReuseRoute`. Additionally,
 * resolvers and most guards for routes do not run unless the path or path params changed
 * (configured by `runGuardsAndResolvers`).
 *
 * @publicApi
 * @see {@link RouteReuseStrategy}
 * @see {@link RunGuardsAndResolvers}
 * @see {@link NavigationBehaviorOptions}
 * @see {@link RouterConfigOptions}
 */
export type OnSameUrlNavigation = 'reload' | 'ignore';

/**
 * The `InjectionToken` and `@Injectable` classes for guards and resolvers are deprecated in favor
 * of plain JavaScript functions instead.. Dependency injection can still be achieved using the
 * [`inject`](api/core/inject) function from `@angular/core` and an injectable class can be used as
 * a functional guard using [`inject`](api/core/inject): `canActivate: [() =>
 * inject(myGuard).canActivate()]`.
 *
 * @deprecated
 * @see {@link CanMatchFn}
 * @see {@link CanLoadFn}
 * @see {@link CanActivateFn}
 * @see {@link CanActivateChildFn}
 * @see {@link CanDeactivateFn}
 * @see {@link ResolveFn}
 * @see {@link core/inject}
 * @publicApi
 */
export type DeprecatedGuard = ProviderToken<any> | any;

/**
 * The supported types that can be returned from a `Router` guard.
 *
 * @see [Routing guide](guide/routing/common-router-tasks#preventing-unauthorized-access)
 * @publicApi
 */
export type GuardResult = boolean | UrlTree | RedirectCommand;

/**
 * Can be returned by a `Router` guard to instruct the `Router` to redirect rather than continue
 * processing the path of the in-flight navigation. The `redirectTo` indicates _where_ the new
 * navigation should go to and the optional `navigationBehaviorOptions` can provide more information
 * about _how_ to perform the navigation.
 *
 * ```ts
 * const route: Route = {
 *   path: "user/:userId",
 *   component: User,
 *   canActivate: [
 *     () => {
 *       const router = inject(Router);
 *       const authService = inject(AuthenticationService);
 *
 *       if (!authService.isLoggedIn()) {
 *         const loginPath = router.parseUrl("/login");
 *         return new RedirectCommand(loginPath, {
 *           skipLocationChange: "true",
 *         });
 *       }
 *
 *       return true;
 *     },
 *   ],
 * };
 * ```
 * @see [Routing guide](guide/routing/common-router-tasks#preventing-unauthorized-access)
 *
 * @publicApi
 */
export class RedirectCommand {
  constructor(
    readonly redirectTo: UrlTree,
    readonly navigationBehaviorOptions?: NavigationBehaviorOptions,
  ) {}
}

/**
 * Type used to represent a value which may be synchronous or async.
 *
 * @publicApi
 */
export type MaybeAsync<T> = T | Observable<T> | Promise<T>;

/**
 * Represents a route configuration for the Router service.
 * An array of `Route` objects, used in `Router.config` and for nested route configurations
 * in `Route.children`.
 *
 * @see {@link Route}
 * @see {@link Router}
 * @see [Router configuration guide](guide/routing/router-reference#configuration)
 * @publicApi
 */
export type Routes = Route[];

/**
 * Represents the result of matching URLs with a custom matching function.
 *
 * * `consumed` is an array of the consumed URL segments.
 * * `posParams` is a map of positional parameters.
 *
 * @see {@link UrlMatcher}
 * @publicApi
 */
export type UrlMatchResult = {
  consumed: UrlSegment[];
  posParams?: {[name: string]: UrlSegment};
};

/**
 * A function for matching a route against URLs. Implement a custom URL matcher
 * for `Route.matcher` when a combination of `path` and `pathMatch`
 * is not expressive enough. Cannot be used together with `path` and `pathMatch`.
 *
 * The function takes the following arguments and returns a `UrlMatchResult` object.
 * * *segments* : An array of URL segments.
 * * *group* : A segment group.
 * * *route* : The route to match against.
 *
 * The following example implementation matches HTML files.
 *
 * ```
 * export function htmlFiles(url: UrlSegment[]) {
 *   return url.length === 1 && url[0].path.endsWith('.html') ? ({consumed: url}) : null;
 * }
 *
 * export const routes = [{ matcher: htmlFiles, component: AnyComponent }];
 * ```
 *
 * @publicApi
 */
export type UrlMatcher = (
  segments: UrlSegment[],
  group: UrlSegmentGroup,
  route: Route,
) => UrlMatchResult | null;

/**
 *
 * Represents static data associated with a particular route.
 *
 * @see {@link Route#data}
 *
 * @publicApi
 */
export type Data = {
  [key: string | symbol]: any;
};

/**
 *
 * Represents the resolved data associated with a particular route.
 *
 * Returning a `RedirectCommand` directs the router to cancel the current navigation and redirect to
 * the location provided in the `RedirectCommand`. Note that there are no ordering guarantees when
 * resolvers execute. If multiple resolvers would return a `RedirectCommand`, only the first one
 * returned will be used.
 *
 * @see {@link Route#resolve}
 *
 * @publicApi
 */
export type ResolveData = {
  [key: string | symbol]: ResolveFn<unknown> | DeprecatedGuard;
};

/**
 * An ES Module object with a default export of the given type.
 *
 * @see {@link Route#loadComponent}
 * @see {@link LoadChildrenCallback}
 *
 * @publicApi
 */
export interface DefaultExport<T> {
  /**
   * Default exports are bound under the name `"default"`, per the ES Module spec:
   * https://tc39.es/ecma262/#table-export-forms-mapping-to-exportentry-records
   */
  default: T;
}

/**
 *
 * A function that is called to resolve a collection of lazy-loaded routes.
 * Must be an arrow function of the following form:
 * `() => import('...').then(mod => mod.MODULE)`
 * or
 * `() => import('...').then(mod => mod.ROUTES)`
 *
 * For example:
 *
 * ```
 * [{
 *   path: 'lazy',
 *   loadChildren: () => import('./lazy-route/lazy.module').then(mod => mod.LazyModule),
 * }];
 * ```
 * or
 * ```
 * [{
 *   path: 'lazy',
 *   loadChildren: () => import('./lazy-route/lazy.routes').then(mod => mod.ROUTES),
 * }];
 * ```
 *
 * If the lazy-loaded routes are exported via a `default` export, the `.then` can be omitted:
 * ```
 * [{
 *   path: 'lazy',
 *   loadChildren: () => import('./lazy-route/lazy.routes'),
 * }];
 * ```
 *
 * @see {@link Route#loadChildren}
 * @publicApi
 */
export type LoadChildrenCallback = () =>
  | Type<any>
  | NgModuleFactory<any>
  | Routes
  | Observable<Type<any> | Routes | DefaultExport<Type<any>> | DefaultExport<Routes>>
  | Promise<
      NgModuleFactory<any> | Type<any> | Routes | DefaultExport<Type<any>> | DefaultExport<Routes>
    >;

/**
 *
 * A function that returns a set of routes to load.
 *
 * @see {@link LoadChildrenCallback}
 * @publicApi
 */
export type LoadChildren = LoadChildrenCallback;

/**
 *
 * How to handle query parameters in a router link.
 * One of:
 * - `"merge"` : Merge new parameters with current parameters.
 * - `"preserve"` : Preserve current parameters.
 * - `"replace"` : Replace current parameters with new parameters. This is the default behavior.
 * - `""` : For legacy reasons, the same as `'replace'`.
 *
 * @see {@link UrlCreationOptions#queryParamsHandling}
 * @see {@link RouterLink}
 * @publicApi
 */
export type QueryParamsHandling = 'merge' | 'preserve' | 'replace' | '';

/**
 * The type for the function that can be used to handle redirects when the path matches a `Route` config.
 *
 * The `RedirectFunction` does have access to the full
 * `ActivatedRouteSnapshot` interface. Some data are not accurately known
 * at the route matching phase. For example, resolvers are not run until
 * later, so any resolved title would not be populated. The same goes for lazy
 * loaded components. This is also true for all the snapshots up to the
 * root, so properties that include parents (root, parent, pathFromRoot)
 * are also excluded. And naturally, the full route matching hasn't yet
 * happened so firstChild and children are not available either.
 *
 * @see {@link Route#redirectTo}
 * @publicApi
 */
export type RedirectFunction = (
  redirectData: Pick<
    ActivatedRouteSnapshot,
    'routeConfig' | 'url' | 'params' | 'queryParams' | 'fragment' | 'data' | 'outlet' | 'title'
  >,
) => string | UrlTree;

/**
 * A policy for when to run guards and resolvers on a route.
 *
 * Guards and/or resolvers will always run when a route is activated or deactivated. When a route is
 * unchanged, the default behavior is the same as `paramsChange`.
 *
 * `paramsChange` : Rerun the guards and resolvers when path or
 * path param changes. This does not include query parameters. This option is the default.
 * - `always` : Run on every execution.
 * - `pathParamsChange` : Rerun guards and resolvers when the path params
 * change. This does not compare matrix or query parameters.
 * - `paramsOrQueryParamsChange` : Run when path, matrix, or query parameters change.
 * - `pathParamsOrQueryParamsChange` : Rerun guards and resolvers when the path params
 * change or query params have changed. This does not include matrix parameters.
 *
 * @see {@link Route#runGuardsAndResolvers}
 * @publicApi
 */
export type RunGuardsAndResolvers =
  | 'pathParamsChange'
  | 'pathParamsOrQueryParamsChange'
  | 'paramsChange'
  | 'paramsOrQueryParamsChange'
  | 'always'
  | ((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);

/**
 * A configuration object that defines a single route.
 * A set of routes are collected in a `Routes` array to define a `Router` configuration.
 * The router attempts to match segments of a given URL against each route,
 * using the configuration options defined in this object.
 *
 * Supports static, parameterized, redirect, and wildcard routes, as well as
 * custom route data and resolve methods.
 *
 * For detailed usage information, see the [Routing Guide](guide/routing/common-router-tasks).
 *
 * @usageNotes
 *
 * ### Simple Configuration
 *
 * The following route specifies that when navigating to, for example,
 * `/team/11/user/bob`, the router creates the 'Team' component
 * with the 'User' child component in it.
 *
 * ```
 * [{
 *   path: 'team/:id',
 *  component: Team,
 *   children: [{
 *     path: 'user/:name',
 *     component: User
 *   }]
 * }]
 * ```
 *
 * ### Multiple Outlets
 *
 * The following route creates sibling components with multiple outlets.
 * When navigating to `/team/11(aux:chat/jim)`, the router creates the 'Team' component next to
 * the 'Chat' component. The 'Chat' component is placed into the 'aux' outlet.
 *
 * ```
 * [{
 *   path: 'team/:id',
 *   component: Team
 * }, {
 *   path: 'chat/:user',
 *   component: Chat
 *   outlet: 'aux'
 * }]
 * ```
 *
 * ### Wild Cards
 *
 * The following route uses wild-card notation to specify a component
 * that is always instantiated regardless of where you navigate to.
 *
 * ```
 * [{
 *   path: '**',
 *   component: WildcardComponent
 * }]
 * ```
 *
 * ### Redirects
 *
 * The following route uses the `redirectTo` property to ignore a segment of
 * a given URL when looking for a child path.
 *
 * When navigating to '/team/11/legacy/user/jim', the router changes the URL segment
 * '/team/11/legacy/user/jim' to '/team/11/user/jim', and then instantiates
 * the Team component with the User child component in it.
 *
 * ```
 * [{
 *   path: 'team/:id',
 *   component: Team,
 *   children: [{
 *     path: 'legacy/user/:name',
 *     redirectTo: 'user/:name'
 *   }, {
 *     path: 'user/:name',
 *     component: User
 *   }]
 * }]
 * ```
 *
 * The redirect path can be relative, as shown in this example, or absolute.
 * If we change the `redirectTo` value in the example to the absolute URL segment '/user/:name',
 * the result URL is also absolute, '/user/jim'.

 * ### Empty Path
 *
 * Empty-path route configurations can be used to instantiate components that do not 'consume'
 * any URL segments.
 *
 * In the following configuration, when navigating to
 * `/team/11`, the router instantiates the 'AllUsers' component.
 *
 * ```
 * [{
 *   path: 'team/:id',
 *   component: Team,
 *   children: [{
 *     path: '',
 *     component: AllUsers
 *   }, {
 *     path: 'user/:name',
 *     component: User
 *   }]
 * }]
 * ```
 *
 * Empty-path routes can have children. In the following example, when navigating
 * to `/team/11/user/jim`, the router instantiates the wrapper component with
 * the user component in it.
 *
 * Note that an empty path route inherits its parent's parameters and data.
 *
 * ```
 * [{
 *   path: 'team/:id',
 *   component: Team,
 *   children: [{
 *     path: '',
 *     component: WrapperCmp,
 *     children: [{
 *       path: 'user/:name',
 *       component: User
 *     }]
 *   }]
 * }]
 * ```
 *
 * ### Matching Strategy
 *
 * The default path-match strategy is 'prefix', which means that the router
 * checks URL elements from the left to see if the URL matches a specified path.
 * For example, '/team/11/user' matches 'team/:id'.
 *
 * ```
 * [{
 *   path: '',
 *   pathMatch: 'prefix', //default
 *   redirectTo: 'main'
 * }, {
 *   path: 'main',
 *   component: Main
 * }]
 * ```
 *
 * You can specify the path-match strategy 'full' to make sure that the path
 * covers the whole unconsumed URL. It is important to do this when redirecting
 * empty-path routes. Otherwise, because an empty path is a prefix of any URL,
 * the router would apply the redirect even when navigating to the redirect destination,
 * creating an endless loop.
 *
 * In the following example, supplying the 'full' `pathMatch` strategy ensures
 * that the router applies the redirect if and only if navigating to '/'.
 *
 * ```
 * [{
 *   path: '',
 *   pathMatch: 'full',
 *   redirectTo: 'main'
 * }, {
 *   path: 'main',
 *   component: Main
 * }]
 * ```
 *
 * ### Componentless Routes
 *
 * You can share parameters between sibling components.
 * For example, suppose that two sibling components should go next to each other,
 * and both of them require an ID parameter. You can accomplish this using a route
 * that does not specify a component at the top level.
 *
 * In the following example, 'MainChild' and 'AuxChild' are siblings.
 * When navigating to 'parent/10/(a//aux:b)', the route instantiates
 * the main child and aux child components next to each other.
 * For this to work, the application component must have the primary and aux outlets defined.
 *
 * ```
 * [{
 *    path: 'parent/:id',
 *    children: [
 *      { path: 'a', component: MainChild },
 *      { path: 'b', component: AuxChild, outlet: 'aux' }
 *    ]
 * }]
 * ```
 *
 * The router merges the parameters, data, and resolve of the componentless
 * parent into the parameters, data, and resolve of the children.
 *
 * This is especially useful when child components are defined
 * with an empty path string, as in the following example.
 * With this configuration, navigating to '/parent/10' creates
 * the main child and aux components.
 *
 * ```
 * [{
 *    path: 'parent/:id',
 *    children: [
 *      { path: '', component: MainChild },
 *      { path: '', component: AuxChild, outlet: 'aux' }
 *    ]
 * }]
 * ```
 *
 * ### Lazy Loading
 *
 * Lazy loading speeds up application load time by splitting the application
 * into multiple bundles and loading them on demand.
 * To use lazy loading, provide the `loadChildren` property in the `Route` object,
 * instead of the `children` property.
 *
 * Given the following example route, the router will lazy load
 * the associated module on demand using the browser native import system.
 *
 * ```
 * [{
 *   path: 'lazy',
 *   loadChildren: () => import('./lazy-route/lazy.module').then(mod => mod.LazyModule),
 * }];
 * ```
 *
 * @publicApi
 */
export interface Route {
  /**
   * Used to define a page title for the route. This can be a static string or an `Injectable` that
   * implements `Resolve`.
   *
   * @see {@link TitleStrategy}
   */
  title?: string | Type<Resolve<string>> | ResolveFn<string>;

  /**
   * The path to match against. Cannot be used together with a custom `matcher` function.
   * A URL string that uses router matching notation.
   * Can be a wild card (`**`) that matches any URL (see Usage Notes below).
   * Default is "/" (the root path).
   *
   */
  path?: string;
  /**
   * The path-matching strategy, one of 'prefix' or 'full'.
   * Default is 'prefix'.
   *
   * By default, the router checks URL elements from the left to see if the URL
   * matches a given path and stops when there is a config match. Importantly there must still be a
   * config match for each segment of the URL. For example, '/team/11/user' matches the prefix
   * 'team/:id' if one of the route's children matches the segment 'user'. That is, the URL
   * '/team/11/user' matches the config
   * `{path: 'team/:id', children: [{path: ':user', component: User}]}`
   * but does not match when there are no children as in `{path: 'team/:id', component: Team}`.
   *
   * The path-match strategy 'full' matches against the entire URL.
   * It is important to do this when redirecting empty-path routes.
   * Otherwise, because an empty path is a prefix of any URL,
   * the router would apply the redirect even when navigating
   * to the redirect destination, creating an endless loop.
   *
   */
  pathMatch?: 'prefix' | 'full';
  /**
   * A custom URL-matching function. Cannot be used together with `path`.
   */
  matcher?: UrlMatcher;
  /**
   * The component to instantiate when the path matches.
   * Can be empty if child routes specify components.
   */
  component?: Type<any>;

  /**
   * An object specifying a lazy-loaded component.
   */
  loadComponent?: () =>
    | Type<unknown>
    | Observable<Type<unknown> | DefaultExport<Type<unknown>>>
    | Promise<Type<unknown> | DefaultExport<Type<unknown>>>;
  /**
   * Filled for routes `loadComponent` once the component is loaded.
   * @internal
   */
  _loadedComponent?: Type<unknown>;

  /**
   * A URL or function that returns a URL to redirect to when the path matches.
   *
   * Absolute if the URL begins with a slash (/) or the function returns a `UrlTree`, otherwise
   * relative to the path URL.
   *
   * The `RedirectFunction` is run in an injection context so it can call `inject` to get any
   * required dependencies.
   *
   * When not present, router does not redirect.
   */
  redirectTo?: string | RedirectFunction;
  /**
   * Name of a `RouterOutlet` object where the component can be placed
   * when the path matches.
   */
  outlet?: string;
  /**
   * An array of `CanActivateFn` or DI tokens used to look up `CanActivate()`
   * handlers, in order to determine if the current user is allowed to
   * activate the component. By default, any user can activate.
   *
   * When using a function rather than DI tokens, the function can call `inject` to get any required
   * dependencies. This `inject` call must be done in a synchronous context.
   */
  canActivate?: Array<CanActivateFn | DeprecatedGuard>;
  /**
   * An array of `CanMatchFn` or DI tokens used to look up `CanMatch()`
   * handlers, in order to determine if the current user is allowed to
   * match the `Route`. By default, any route can match.
   *
   * When using a function rather than DI tokens, the function can call `inject` to get any required
   * dependencies. This `inject` call must be done in a synchronous context.
   */
  canMatch?: Array<CanMatchFn | DeprecatedGuard>;
  /**
   * An array of `CanActivateChildFn` or DI tokens used to look up `CanActivateChild()` handlers,
   * in order to determine if the current user is allowed to activate
   * a child of the component. By default, any user can activate a child.
   *
   * When using a function rather than DI tokens, the function can call `inject` to get any required
   * dependencies. This `inject` call must be done in a synchronous context.
   */
  canActivateChild?: Array<CanActivateChildFn | DeprecatedGuard>;
  /**
   * An array of `CanDeactivateFn` or DI tokens used to look up `CanDeactivate()`
   * handlers, in order to determine if the current user is allowed to
   * deactivate the component. By default, any user can deactivate.
   *
   * When using a function rather than DI tokens, the function can call `inject` to get any required
   * dependencies. This `inject` call must be done in a synchronous context.
   */
  canDeactivate?: Array<CanDeactivateFn<any> | DeprecatedGuard>;
  /**
   * An array of `CanLoadFn` or DI tokens used to look up `CanLoad()`
   * handlers, in order to determine if the current user is allowed to
   * load the component. By default, any user can load.
   *
   * When using a function rather than DI tokens, the function can call `inject` to get any required
   * dependencies. This `inject` call must be done in a synchronous context.
   * @deprecated Use `canMatch` instead
   */
  canLoad?: Array<CanLoadFn | DeprecatedGuard>;
  /**
   * Additional developer-defined data provided to the component via
   * `ActivatedRoute`. By default, no additional data is passed.
   */
  data?: Data;
  /**
   * A map of DI tokens used to look up data resolvers. See `Resolve`.
   */
  resolve?: ResolveData;
  /**
   * An array of child `Route` objects that specifies a nested route
   * configuration.
   */
  children?: Routes;
  /**
   * An object specifying lazy-loaded child routes.
   */
  loadChildren?: LoadChildren;

  /**
   * A policy for when to run guards and resolvers on a route.
   *
   * Guards and/or resolvers will always run when a route is activated or deactivated. When a route
   * is unchanged, the default behavior is the same as `paramsChange`.
   *
   * `paramsChange` : Rerun the guards and resolvers when path or
   * path param changes. This does not include query parameters. This option is the default.
   * - `always` : Run on every execution.
   * - `pathParamsChange` : Rerun guards and resolvers when the path params
   * change. This does not compare matrix or query parameters.
   * - `paramsOrQueryParamsChange` : Run when path, matrix, or query parameters change.
   * - `pathParamsOrQueryParamsChange` : Rerun guards and resolvers when the path params
   * change or query params have changed. This does not include matrix parameters.
   *
   * @see {@link RunGuardsAndResolvers}
   */
  runGuardsAndResolvers?: RunGuardsAndResolvers;

  /**
   * A `Provider` array to use for this `Route` and its `children`.
   *
   * The `Router` will create a new `EnvironmentInjector` for this
   * `Route` and use it for this `Route` and its `children`. If this
   * route also has a `loadChildren` function which returns an `NgModuleRef`, this injector will be
   * used as the parent of the lazy loaded module.
   */
  providers?: Array<Provider | EnvironmentProviders>;

  /**
   * Injector created from the static route providers
   * @internal
   */
  _injector?: EnvironmentInjector;

  /**
   * Filled for routes with `loadChildren` once the routes are loaded.
   * @internal
   */
  _loadedRoutes?: Route[];

  /**
   * Filled for routes with `loadChildren` once the routes are loaded
   * @internal
   */
  _loadedInjector?: EnvironmentInjector;
}

export interface LoadedRouterConfig {
  routes: Route[];
  injector: EnvironmentInjector | undefined;
}

/**
 * @description
 *
 * Interface that a class can implement to be a guard deciding if a route can be activated.
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements a `CanActivate` function that checks whether the
 * current user has permission to activate the requested route.
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canActivate(): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanActivateTeam implements CanActivate {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canActivate(
 *     route: ActivatedRouteSnapshot,
 *     state: RouterStateSnapshot
 *   ): MaybeAsync<GuardResult> {
 *     return this.permissions.canActivate(this.currentUser, route.params.id);
 *   }
 * }
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamComponent,
 *         canActivate: [CanActivateTeam]
 *       }
 *     ])
 *   ],
 *   providers: [CanActivateTeam, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * @publicApi
 */
export interface CanActivate {
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MaybeAsync<GuardResult>;
}

/**
 * The signature of a function used as a `canActivate` guard on a `Route`.
 *
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements and uses a `CanActivateFn` that checks whether the
 * current user has permission to activate the requested route.
 *
 * ```ts
 * @Injectable()
 * class UserToken {}
 *
 * @Injectable()
 * class PermissionsService {
 *   canActivate(currentUser: UserToken, userId: string): boolean {
 *     return true;
 *   }
 *   canMatch(currentUser: UserToken): boolean {
 *     return true;
 *   }
 * }
 *
 * const canActivateTeam: CanActivateFn = (
 *   route: ActivatedRouteSnapshot,
 *   state: RouterStateSnapshot,
 * ) => {
 *   return inject(PermissionsService).canActivate(inject(UserToken), route.params['id']);
 * };
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```ts
 * bootstrapApplication(App, {
 *    providers: [
 *      provideRouter([
 *        {
 *          path: 'team/:id',
 *          component: TeamComponent,
 *          canActivate: [canActivateTeam],
 *        },
 *      ]),
 *    ],
 *  });
 * ```
 *
 * @publicApi
 * @see {@link Route}
 */
export type CanActivateFn = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot,
) => MaybeAsync<GuardResult>;

/**
 * @description
 *
 * Interface that a class can implement to be a guard deciding if a child route can be activated.
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements a `CanActivateChild` function that checks whether the
 * current user has permission to activate the requested child route.
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canActivate(user: UserToken, id: string): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanActivateTeam implements CanActivateChild {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canActivateChild(
 *     route: ActivatedRouteSnapshot,
 *     state: RouterStateSnapshot
 *   ): MaybeAsync<GuardResult> {
 *     return this.permissions.canActivate(this.currentUser, route.params.id);
 *   }
 * }
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'root',
 *         canActivateChild: [CanActivateTeam],
 *         children: [
 *           {
 *              path: 'team/:id',
 *              component: TeamComponent
 *           }
 *         ]
 *       }
 *     ])
 *   ],
 *   providers: [CanActivateTeam, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * @publicApi
 */
export interface CanActivateChild {
  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot,
  ): MaybeAsync<GuardResult>;
}

/**
 * The signature of a function used as a `canActivateChild` guard on a `Route`.
 *
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements a `canActivate` function that checks whether the
 * current user has permission to activate the requested route.
 *
 * {@example router/route_functional_guards.ts region="CanActivateChildFn"}
 *
 * @publicApi
 * @see {@link Route}
 */
export type CanActivateChildFn = (
  childRoute: ActivatedRouteSnapshot,
  state: RouterStateSnapshot,
) => MaybeAsync<GuardResult>;

/**
 * @description
 *
 * Interface that a class can implement to be a guard deciding if a route can be deactivated.
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements a `CanDeactivate` function that checks whether the
 * current user has permission to deactivate the requested route.
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canDeactivate(user: UserToken, id: string): boolean {
 *     return true;
 *   }
 * }
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```
 *
 * @Injectable()
 * class CanDeactivateTeam implements CanDeactivate<TeamComponent> {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canDeactivate(
 *     component: TeamComponent,
 *     currentRoute: ActivatedRouteSnapshot,
 *     currentState: RouterStateSnapshot,
 *     nextState: RouterStateSnapshot
 *   ): MaybeAsync<GuardResult> {
 *     return this.permissions.canDeactivate(this.currentUser, route.params.id);
 *   }
 * }
 *
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamComponent,
 *         canDeactivate: [CanDeactivateTeam]
 *       }
 *     ])
 *   ],
 *   providers: [CanDeactivateTeam, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * @publicApi
 */
export interface CanDeactivate<T> {
  canDeactivate(
    component: T,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState: RouterStateSnapshot,
  ): MaybeAsync<GuardResult>;
}

/**
 * The signature of a function used as a `canDeactivate` guard on a `Route`.
 *
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, the current navigation
 * is cancelled and a new navigation begins to the `UrlTree` returned from the guard.
 *
 * The following example implements and uses a `CanDeactivateFn` that checks whether the
 * user component has unsaved changes before navigating away from the route.
 *
 * {@example router/route_functional_guards.ts region="CanDeactivateFn"}
 *
 * @publicApi
 * @see {@link Route}
 */
export type CanDeactivateFn<T> = (
  component: T,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState: RouterStateSnapshot,
) => MaybeAsync<GuardResult>;

/**
 * @description
 *
 * Interface that a class can implement to be a guard deciding if a `Route` can be matched.
 * If all guards return `true`, navigation continues and the `Router` will use the `Route` during
 * activation. If any guard returns `false`, the `Route` is skipped for matching and other `Route`
 * configurations are processed instead.
 *
 * The following example implements a `CanMatch` function that decides whether the
 * current user has permission to access the users page.
 *
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canAccess(user: UserToken, route: Route, segments: UrlSegment[]): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanMatchTeamSection implements CanMatch {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canMatch(route: Route, segments: UrlSegment[]): Observable<boolean>|Promise<boolean>|boolean {
 *     return this.permissions.canAccess(this.currentUser, route, segments);
 *   }
 * }
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```
 *
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamComponent,
 *         loadChildren: () => import('./team').then(mod => mod.TeamModule),
 *         canMatch: [CanMatchTeamSection]
 *       },
 *       {
 *         path: '**',
 *         component: NotFoundComponent
 *       }
 *     ])
 *   ],
 *   providers: [CanMatchTeamSection, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * If the `CanMatchTeamSection` were to return `false`, the router would continue navigating to the
 * `team/:id` URL, but would load the `NotFoundComponent` because the `Route` for `'team/:id'`
 * could not be used for a URL match but the catch-all `**` `Route` did instead.
 *
 * @publicApi
 */
export interface CanMatch {
  canMatch(route: Route, segments: UrlSegment[]): MaybeAsync<GuardResult>;
}

/**
 * The signature of a function used as a `canMatch` guard on a `Route`.
 *
 * If all guards return `true`, navigation continues and the `Router` will use the `Route` during
 * activation. If any guard returns `false`, the `Route` is skipped for matching and other `Route`
 * configurations are processed instead.
 *
 * The following example implements and uses a `CanMatchFn` that checks whether the
 * current user has permission to access the team page.
 *
 * {@example router/route_functional_guards.ts region="CanMatchFn"}
 *
 * @publicApi
 * @see {@link Route}
 */
export type CanMatchFn = (route: Route, segments: UrlSegment[]) => MaybeAsync<GuardResult>;

/**
 * @description
 *
 * Interface that classes can implement to be a data provider.
 * A data provider class can be used with the router to resolve data during navigation.
 * The interface defines a `resolve()` method that is invoked right after the `ResolveStart`
 * router event. The router waits for the data to be resolved before the route is finally activated.
 *
 * The following example implements a `resolve()` method that retrieves the data
 * needed to activate the requested route.
 *
 * ```
 * @Injectable({ providedIn: 'root' })
 * export class HeroResolver implements Resolve<Hero> {
 *   constructor(private service: HeroService) {}
 *
 *   resolve(
 *     route: ActivatedRouteSnapshot,
 *     state: RouterStateSnapshot
 *   ): Observable<Hero>|Promise<Hero>|Hero {
 *     return this.service.getHero(route.paramMap.get('id'));
 *   }
 * }
 * ```
 *
 * Here, the defined `resolve()` function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```

 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'detail/:id',
 *         component: HeroDetailComponent,
 *         resolve: {
 *           hero: HeroResolver
 *         }
 *       }
 *     ])
 *   ],
 *   exports: [RouterModule]
 * })
 * export class AppRoutingModule {}
 * ```
 *
 * And you can access to your resolved data from `HeroComponent`:
 *
 * ```
 * @Component({
 *  selector: "app-hero",
 *  templateUrl: "hero.component.html",
 * })
 * export class HeroComponent {
 *
 *  constructor(private activatedRoute: ActivatedRoute) {}
 *
 *  ngOnInit() {
 *    this.activatedRoute.data.subscribe(({ hero }) => {
 *      // do something with your resolved data ...
 *    })
 *  }
 *
 * }
 * ```
 *
 * @usageNotes
 *
 * When both guard and resolvers are specified, the resolvers are not executed until
 * all guards have run and succeeded.
 * For example, consider the following route configuration:
 *
 * ```
 * {
 *  path: 'base'
 *  canActivate: [BaseGuard],
 *  resolve: {data: BaseDataResolver}
 *  children: [
 *   {
 *     path: 'child',
 *     guards: [ChildGuard],
 *     component: ChildComponent,
 *     resolve: {childData: ChildDataResolver}
 *    }
 *  ]
 * }
 * ```
 * The order of execution is: BaseGuard, ChildGuard, BaseDataResolver, ChildDataResolver.
 *
 * @publicApi
 * @see {@link ResolveFn}
 */
export interface Resolve<T> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MaybeAsync<T>;
}

/**
 * Function type definition for a data provider.
 *
 * A data provider can be used with the router to resolve data during navigation.
 * The router waits for the data to be resolved before the route is finally activated.
 *
 * A resolver can also redirect a `RedirectCommand` and the Angular router will use
 * it to redirect the current navigation to the new destination.
 *
 * @usageNotes
 *
 * The following example implements a function that retrieves the data
 * needed to activate the requested route.
 *
 * ```ts
 * interface Hero {
 *   name: string;
 * }
 * @Injectable()
 * export class HeroService {
 *   getHero(id: string) {
 *     return {name: `Superman-${id}`};
 *   }
 * }
 *
 * export const heroResolver: ResolveFn<Hero> = (
 *   route: ActivatedRouteSnapshot,
 *   state: RouterStateSnapshot,
 * ) => {
 *   return inject(HeroService).getHero(route.paramMap.get('id')!);
 * };
 *
 * bootstrapApplication(App, {
 *   providers: [
 *     provideRouter([
 *       {
 *         path: 'detail/:id',
 *         component: HeroDetailComponent,
 *         resolve: {hero: heroResolver},
 *       },
 *     ]),
 *   ],
 * });
 * ```
 *
 * And you can access to your resolved data from `HeroComponent`:
 *
 * ```ts
 * @Component({template: ''})
 * export class HeroDetailComponent {
 *   private activatedRoute = inject(ActivatedRoute);
 *
 *   ngOnInit() {
 *     this.activatedRoute.data.subscribe(({hero}) => {
 *       // do something with your resolved data ...
 *     });
 *   }
 * }
 * ```
 *
 * If resolved data cannot be retrieved, you may want to redirect the user
 * to a new page instead:
 *
 * ```ts
 * export const heroResolver: ResolveFn<Hero> = async (
 *   route: ActivatedRouteSnapshot,
 *   state: RouterStateSnapshot,
 * ) => {
 *   const router = inject(Router);
 *   const heroService = inject(HeroService);
 *   try {
 *     return await heroService.getHero(route.paramMap.get('id')!);
 *   } catch {
 *     return new RedirectCommand(router.parseUrl('/404'));
 *   }
 * };
 * ```
 *
 * When both guard and resolvers are specified, the resolvers are not executed until
 * all guards have run and succeeded.
 * For example, consider the following route configuration:
 *
 * ```
 * {
 *  path: 'base'
 *  canActivate: [baseGuard],
 *  resolve: {data: baseDataResolver}
 *  children: [
 *   {
 *     path: 'child',
 *     canActivate: [childGuard],
 *     component: ChildComponent,
 *     resolve: {childData: childDataResolver}
 *    }
 *  ]
 * }
 * ```
 * The order of execution is: baseGuard, childGuard, baseDataResolver, childDataResolver.
 *
 * @publicApi
 * @see {@link Route}
 */
export type ResolveFn<T> = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot,
) => MaybeAsync<T | RedirectCommand>;

/**
 * @description
 *
 * Interface that a class can implement to be a guard deciding if children can be loaded.
 * If all guards return `true`, navigation continues. If any guard returns `false`,
 * navigation is cancelled. If any guard returns a `UrlTree`, current navigation
 * is cancelled and a new navigation starts to the `UrlTree` returned from the guard.
 *
 * The following example implements a `CanLoad` function that decides whether the
 * current user has permission to load requested child routes.
 *
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canLoadChildren(user: UserToken, id: string, segments: UrlSegment[]): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanLoadTeamSection implements CanLoad {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canLoad(route: Route, segments: UrlSegment[]): Observable<boolean>|Promise<boolean>|boolean {
 *     return this.permissions.canLoadChildren(this.currentUser, route, segments);
 *   }
 * }
 * ```
 *
 * Here, the defined guard function is provided as part of the `Route` object
 * in the router configuration:
 *
 * ```
 *
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamComponent,
 *         loadChildren: () => import('./team').then(mod => mod.TeamModule),
 *         canLoad: [CanLoadTeamSection]
 *       }
 *     ])
 *   ],
 *   providers: [CanLoadTeamSection, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * @publicApi
 * @deprecated Use {@link CanMatch} instead
 */
export interface CanLoad {
  canLoad(route: Route, segments: UrlSegment[]): MaybeAsync<GuardResult>;
}

/**
 * The signature of a function used as a `canLoad` guard on a `Route`.
 *
 * @publicApi
 * @see {@link CanLoad}
 * @see {@link Route}
 * @see {@link CanMatch}
 * @deprecated Use `Route.canMatch` and `CanMatchFn` instead
 */
export type CanLoadFn = (route: Route, segments: UrlSegment[]) => MaybeAsync<GuardResult>;

/**
 * @description
 *
 * Options that modify the `Router` navigation strategy.
 * Supply an object containing any of these properties to a `Router` navigation function to
 * control how the navigation should be handled.
 *
 * @see {@link Router#navigate}
 * @see {@link Router#navigateByUrl}
 * @see [Routing and Navigation guide](guide/routing/common-router-tasks)
 *
 * @publicApi
 */
export interface NavigationBehaviorOptions {
  /**
   * How to handle a navigation request to the current URL.
   *
   * This value is a subset of the options available in `OnSameUrlNavigation` and
   * will take precedence over the default value set for the `Router`.
   *
   * @see {@link OnSameUrlNavigation}
   * @see {@link RouterConfigOptions}
   */
  onSameUrlNavigation?: OnSameUrlNavigation;

  /**
   * When true, navigates without pushing a new state into history.
   *
   * ```
   * // Navigate silently to /view
   * this.router.navigate(['/view'], { skipLocationChange: true });
   * ```
   */
  skipLocationChange?: boolean;

  /**
   * When true, navigates while replacing the current state in history.
   *
   * ```
   * // Navigate to /view
   * this.router.navigate(['/view'], { replaceUrl: true });
   * ```
   */
  replaceUrl?: boolean;

  /**
   * Developer-defined state that can be passed to any navigation.
   * Access this value through the `Navigation.extras` object
   * returned from the [Router.getCurrentNavigation()
   * method](api/router/Router#getcurrentnavigation) while a navigation is executing.
   *
   * After a navigation completes, the router writes an object containing this
   * value together with a `navigationId` to `history.state`.
   * The value is written when `location.go()` or `location.replaceState()`
   * is called before activating this route.
   *
   * Note that `history.state` does not pass an object equality test because
   * the router adds the `navigationId` on each navigation.
   *
   */
  state?: {[k: string]: any};

  /**
   * Use this to convey transient information about this particular navigation, such as how it
   * happened. In this way, it's different from the persisted value `state` that will be set to
   * `history.state`. This object is assigned directly to the Router's current `Navigation`
   * (it is not copied or cloned), so it should be mutated with caution.
   *
   * One example of how this might be used is to trigger different single-page navigation animations
   * depending on how a certain route was reached. For example, consider a photo gallery app, where
   * you can reach the same photo URL and state via various routes:
   *
   * - Clicking on it in a gallery view
   * - Clicking
   * - "next" or "previous" when viewing another photo in the album
   * - Etc.
   *
   * Each of these wants a different animation at navigate time. This information doesn't make sense
   * to store in the persistent URL or history entry state, but it's still important to communicate
   * from the rest of the application, into the router.
   *
   * This information could be used in coordination with the View Transitions feature and the
   * `onViewTransitionCreated` callback. The information might be used in the callback to set
   * classes on the document in order to control the transition animations and remove the classes
   * when the transition has finished animating.
   */
  readonly info?: unknown;

  /**
   * When set, the Router will update the browser's address bar to match the given `UrlTree` instead
   * of the one used for route matching.
   *
   *
   * @usageNotes
   *
   * This feature is useful for redirects, such as redirecting to an error page, without changing
   * the value that will be displayed in the browser's address bar.
   *
   * ```
   * const canActivate: CanActivateFn = (route: ActivatedRouteSnapshot) => {
   *   const userService = inject(UserService);
   *   const router = inject(Router);
   *   if (!userService.isLoggedIn()) {
   *     const targetOfCurrentNavigation = router.getCurrentNavigation()?.finalUrl;
   *     const redirect = router.parseUrl('/404');
   *     return new RedirectCommand(redirect, {browserUrl: targetOfCurrentNavigation});
   *   }
   *   return true;
   * };
   * ```
   *
   * This value is used directly, without considering any `UrlHandingStrategy`. In this way,
   * `browserUrl` can also be used to use a different value for the browser URL than what would have
   * been produced by from the navigation due to `UrlHandlingStrategy.merge`.
   *
   * This value only affects the path presented in the browser's address bar. It does not apply to
   * the internal `Router` state. Information such as `params` and `data` will match the internal
   * state used to match routes which will be different from the browser URL when using this feature
   * The same is true when using other APIs that cause the browser URL the differ from the Router
   * state, such as `skipLocationChange`.
   */
  readonly browserUrl?: UrlTree | string;
}
