/*
 * Copyright (C) 2018 Robert Bosch GmbH Copyright (C) 2018 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { Type } from '@angular/core';
import { fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
import {
  ActivatedRoute,
  ActivatedRouteSnapshot,
  Data,
  ParamMap,
  Params,
  Route,
  UrlSegment,
} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { defer, Observable } from 'rxjs';
import { BreadcrumbsService } from './breadcrumbs.service';
import { CoreConfig } from './core-configuration/core-config';
import { CORE_CONFIG } from './core-configuration/core-config.token';
import { SidebarNavItem } from './layout/bci-sidebar/sidebar-nav-item.model';
import { NavigationService } from './navigation/navigation.service';

/** Create async observable that emits-once and completes
 *  after a JS engine turn */
export function asyncData<T>(data: T) {
  return defer(() => Promise.resolve(data));
}

describe('BreadcrumbsService', () => {
  let breadcrumbsService: BreadcrumbsService;
  let navigationService: NavigationService;
  let navigationServiceSpy: jest.SpyInstance<Observable<SidebarNavItem[]>>;
  let activatedGlobalRoute: ActivatedRoute;
  const navigationStub = [
    {
      title: 'Home',
      url: '/',
      position: 0,
      icon: 'Bosch-Ic-home-outline',
      routerLinkActiveOptions: {
        exact: true,
      },
    },
    {
      title: 'Colors',
      url: 'colors',
      position: 2,
      icon: 'Bosch-Ic-edit',
    },
    {
      title: 'Components',
      url: 'customComponents',
      icon: 'Bosch-Ic-components',
      items: [
        {
          title: 'Overview',
          routerLinkActiveOptions: {
            exact: true,
          },
          url: 'customComponents',
        },
        {
          title: 'Login Component',
          url: 'login',
        },
        {
          title: 'Tiles',
          url: 'customComponents/tiles',
        },
        {
          title: 'Commandbar',
          url: 'customComponents/commandbar',
        },
        {
          title: 'Breadcrumbs',
          url: 'customComponents/breadcrumbs',
        },
      ],
    },
    {
      title: 'Master Detail Pattern',
      url: 'master-detail',
      icon: 'Bosch-Ic-components',
      items: [
        {
          title: 'List',
          url: 'master-detail/list',
        },
        {
          title: 'Tiles',
          url: 'master-detail/tiles',
        },
        {
          title: 'Table',
          url: 'master-detail/table',
        },
      ],
    },
    {
      title: 'Unlinked Level 1',
      icon: 'Bosch-Ic-components',
      items: [
        {
          title: 'Level 2',
          url: 'level1/level2',
        },
      ],
    },
  ];

  beforeEach(() => {
    const navSpy: Partial<NavigationService> = {
      getNavigationItems: jest.fn(),
    };

    TestBed.configureTestingModule({
      imports: [RouterTestingModule.withRoutes([])],
      providers: [
        { provide: NavigationService, useValue: navSpy },
        { provide: CORE_CONFIG, useValue: <CoreConfig>{ environment: 'test', endpointNavigation: 'nix' } },
        {
          provide: ActivatedRoute,
          useValue: <ActivatedRoute>{
            snapshot: <ActivatedRouteSnapshot>{
              title: '',
              url: <UrlSegment[]>[],
            },
          },
        },
        BreadcrumbsService,
      ],
    });

    navigationService = TestBed.inject(NavigationService);
    navigationServiceSpy = jest
      .spyOn(navigationService, 'getNavigationItems')
      .mockReturnValue(asyncData(navigationStub));
    activatedGlobalRoute = TestBed.inject(ActivatedRoute);

    breadcrumbsService = TestBed.inject(BreadcrumbsService);
    breadcrumbsService.setNavigationItems(navigationStub);
  });

  it('should be created', inject([BreadcrumbsService], (service: BreadcrumbsService) => {
    expect(service).toBeTruthy();
  }));

  it(
    'should not use navigation service to get navigation items',
    waitForAsync(() => {
      // 'navigation spy method was not called'
      expect(navigationServiceSpy).toHaveBeenCalledTimes(0);
    })
  );

  it('should match Home navigation item for root', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [],
      },
    };
    breadcrumbsService.refresh();
    tick();
    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Home']);
      // 'and not linked'
      expect(breadcrumbs[0].url).toBe(undefined);
    });
  }));

  it('should match single path segment to navigation items', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'colors' }],
      },
    };
    breadcrumbsService.refresh();
    tick();

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Colors']);
      // 'and not linked'
      expect(breadcrumbs[0].url).toBe(undefined);
    });
  }));

  it('should have current and parent navigation item', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'customComponents' }],
        firstChild: {
          url: [{ path: 'tiles' }],
        },
      },
    };
    breadcrumbsService.refresh();
    tick();

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Components', 'Tiles']);
      // 'and linked'
      expect(breadcrumbs[0].url).toBe('customComponents');
      // 'and not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should not link unlinked navigation items', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2' }],
        },
      },
    };
    breadcrumbsService.refresh();
    tick();

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2']);
      // 'not linked'
      expect(breadcrumbs[0].url).toBe(undefined);
    });
  }));

  it('should match parent when on child navigation item with same url ("Overview Page Pattern")', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'customComponents' }],
      },
    };
    breadcrumbsService.refresh();
    tick();

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Components']);
      // 'and not linked'
      expect(breadcrumbs[0].url).toBe(undefined);
    });
  }));

  it('should link last breadcrumb if subroute given', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2' }],
          firstChild: {
            url: [{ path: 'level3' }],
          },
        },
      },
    };
    breadcrumbsService.refresh();
    tick();

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'and not linked');
      // expect(breadcrumbs[1].url).toBe('level1/level2', 'and linked');
      // 'and linked'
      expect(breadcrumbs[0].url).toBe('level1/level2');
    });
  }));

  it('should consider additional static breadcrumb definitions', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2' }],
          firstChild: {
            url: [{ path: 'level3' }],
          },
        },
      },
    };
    breadcrumbsService.setTitle('level1/level2/level3', 'Level 3');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2', 'Level 3']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('level1/level2', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2', 'Level 3']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('level1/level2');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider additional temporary breadcrumb definitions over static ones', fakeAsync(() => {
    activatedGlobalRoute.snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2' }],
          firstChild: {
            url: [{ path: 'level3' }],
          },
        },
      },
    };
    breadcrumbsService.setTemporaryTitle('level1/level2/level3', 'Level 3 Temp');
    breadcrumbsService.setTitle('level1/level2/level3', 'Level 3');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2', 'Level 3 Temp']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('level1/level2', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2', 'Level 3 Temp']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('level1/level2');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for route', fakeAsync(() => {
    const snapshot = new ActivatedRouteSnapshotMock({
      url: [],
      firstChild: new ActivatedRouteSnapshotMock({
        url: [new UrlSegment('level1', {})],
        firstChild: new ActivatedRouteSnapshotMock({
          url: [new UrlSegment('level2', {})],
          firstChild: new ActivatedRouteSnapshotMock({
            url: [new UrlSegment('level3', {})],
          }),
        }),
      }),
    });
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild.firstChild,
      pathFromRoot: [
        snapshot,
        snapshot.firstChild,
        snapshot.firstChild.firstChild,
        snapshot.firstChild.firstChild.firstChild,
      ],
    });

    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Level 3 Activated Route');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2', 'Level 3 Activated Route']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('level1/level2', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2', 'Level 3 Activated Route']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('level1/level2');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for route including routing configurations with empty path', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        routeConfig: {
          path: 'level1',
        },
        firstChild: {
          url: [],
          routeConfig: {
            path: '',
          },
          firstChild: {
            url: [{ path: 'level2' }],
            routeConfig: {
              path: 'level2',
            },
          },
        },
      },
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild.firstChild,
      pathFromRoot: [
        snapshot,
        snapshot.firstChild,
        snapshot.firstChild.firstChild,
        snapshot.firstChild.firstChild.firstChild,
      ],
    });

    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Test');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Test']);
    });
  }));

  it('should consider temporary breadcrumb definitions for partial explicit route', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2.1' }, { path: 'level2.2' }],
        },
      },
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild,
      pathFromRoot: [snapshot, snapshot.firstChild, snapshot.firstChild.firstChild],
    });

    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Level 2 Part 2');
    breadcrumbsService.setTemporaryTitle('level1/level2.1', 'Level 2 Part 1');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2 Part 1', 'Level 2 Part 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('level1/level2.1', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2 Part 1', 'Level 2 Part 2']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('level1/level2.1');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for partial explicit route of root route', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [{ path: 'level.1' }, { path: 'level.2' }],
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot,
      pathFromRoot: [snapshot],
      parent: <ActivatedRouteSnapshot>{
        title: '',
        url: [],
        pathFromRoot: [],
      },
    });

    breadcrumbsService.setTemporaryTitleForPartialRoute(activateComponentRouteSnapshot, ['level.1'], 'Part 1');
    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Part 2');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Part 1', 'Part 2']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('/level.1');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for partial route defined by required segments', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2.1' }, { path: 'level2.2' }],
        },
      },
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild,
      pathFromRoot: [snapshot, snapshot.firstChild, snapshot.firstChild.firstChild],
      parent: new ActivatedRouteSnapshotMock({
        ...snapshot.firstChild,
        pathFromRoot: [snapshot, snapshot.firstChild],
      }),
    });

    breadcrumbsService.setTemporaryTitleForPartialRoute(activateComponentRouteSnapshot, ['level2.1'], 'Level 2 Part 1');
    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Level 2 Part 2');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2 Part 1', 'Level 2 Part 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('/level1/level2.1', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2 Part 1', 'Level 2 Part 2']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('/level1/level2.1');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for partial route defined by required parameters', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2.0' }, { path: 'level2.1' }, { path: 'level2.2' }],
          routeConfig: {
            path: ':level2Parameter0/:level2Parameter1/level2.2',
          },
        },
      },
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild,
      pathFromRoot: [snapshot, snapshot.firstChild, snapshot.firstChild.firstChild],
      parent: new ActivatedRouteSnapshotMock({
        ...snapshot.firstChild,
        pathFromRoot: [snapshot, snapshot.firstChild],
      }),
    });

    breadcrumbsService.setTemporaryTitleForPartialRoute(
      activateComponentRouteSnapshot,
      [':level2Parameter0', ':level2Parameter1'],
      'Level 2 Part 1'
    );
    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Level 2 Part 2');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2 Part 1', 'Level 2 Part 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('/level1/level2.0/level2.1', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2 Part 1', 'Level 2 Part 2']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('/level1/level2.0/level2.1');
      // 'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));

  it('should consider temporary breadcrumb definitions for partial route defined by required parameters skipping a non-required one', fakeAsync(() => {
    const snapshot = <ActivatedRouteSnapshot>{
      title: '',
      url: [],
      firstChild: {
        url: [{ path: 'level1' }],
        firstChild: {
          url: [{ path: 'level2.0' }, { path: 'level2.1' }, { path: 'level2.2' }],
          routeConfig: {
            path: ':level2Parameter0/:level2Parameter1/level2.2',
          },
        },
      },
    };
    activatedGlobalRoute.snapshot = snapshot;
    const activateComponentRouteSnapshot = new ActivatedRouteSnapshotMock({
      ...snapshot.firstChild.firstChild,
      pathFromRoot: [snapshot, snapshot.firstChild, snapshot.firstChild.firstChild],
      parent: new ActivatedRouteSnapshotMock({
        ...snapshot.firstChild,
        pathFromRoot: [snapshot, snapshot.firstChild],
      }),
    });

    breadcrumbsService.setTemporaryTitleForPartialRoute(
      activateComponentRouteSnapshot,
      [':level2Parameter1'],
      'Level 2 Part 1'
    );
    breadcrumbsService.setTemporaryTitleForRoute(activateComponentRouteSnapshot, 'Level 2 Part 2');

    breadcrumbsService.breadcrumbs.subscribe((breadcrumbs) => {
      // TODO: re-enable tests when breadcrumbsService uses parents
      // expect(breadcrumbs.map(b => b.title)).toEqual(['Unlinked Level 1', 'Level 2 Part 1', 'Level 2 Part 2']);
      // expect(breadcrumbs[0].url).toBe(undefined, 'not linked');
      // expect(breadcrumbs[1].url).toBe('/level1/level2.0/level2.1', 'linked');
      // expect(breadcrumbs[2].url).toBe(undefined, 'not linked');
      expect(breadcrumbs.map((b) => b.title)).toEqual(['Level 2 Part 1', 'Level 2 Part 2']);
      // 'linked'
      expect(breadcrumbs[0].url).toBe('/level1/level2.0/level2.1');
      //  'not linked'
      expect(breadcrumbs[1].url).toBe(undefined);
    });
  }));
});

class ActivatedRouteSnapshotMock implements ActivatedRouteSnapshot {
  /** The resolved route title */
  title: string | undefined;
  /** The URL segments matched by this route */
  url: UrlSegment[];
  /** The matrix parameters scoped to this route */
  params: Params;
  /** The query parameters shared by all the routes */
  queryParams: Params;
  /** The URL fragment shared by all the routes */
  fragment: string;
  /** The static and resolved data of this route */
  data: Data;
  /** The outlet name of the route */
  outlet: string;
  /** The component of the route */
  component: Type<any> | null;
  /** The configuration used to match this route **/
  _routeConfig: Route;
  _firstChild: ActivatedRouteSnapshot;
  _parent: ActivatedRouteSnapshot;
  _pathFromRoot: ActivatedRouteSnapshot[];

  constructor(params: {
    url?: UrlSegment[];
    routeConfig?: Route;
    firstChild?: ActivatedRouteSnapshot;
    pathFromRoot?: ActivatedRouteSnapshot[];
    parent?: ActivatedRouteSnapshot;
  }) {
    if (params.routeConfig) {
      this._routeConfig = params.routeConfig;
    }

    if (params.url) {
      this.url = params.url;
    }

    if (params.firstChild) {
      this._firstChild = params.firstChild;
    }

    if (params.parent) {
      this._parent = params.parent;
    }

    if (params.pathFromRoot) {
      this._pathFromRoot = params.pathFromRoot;
    }
  }

  /** The root of the router state */
  get root(): ActivatedRouteSnapshot {
    return undefined;
  }

  /** The parent of this route in the router state tree */
  get parent(): ActivatedRouteSnapshot | null {
    return this._parent;
  }

  /** The first child of this route in the router state tree */
  /** The children of this route in the router state tree */
  get children(): ActivatedRouteSnapshot[] {
    return [];
  }

  /** The path from the root of the router state tree to this route */
  get paramMap(): ParamMap {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    return new (class implements ParamMap {
      readonly keys: string[];

      get(name: string): string | null {
        return that.params[name];
      }

      getAll(name: string): string[] {
        return that.params[name];
      }

      has(name: string): boolean {
        return !!that.params[name];
      }
    })();
  }

  get queryParamMap(): ParamMap {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    return new (class implements ParamMap {
      readonly keys: string[];

      get(name: string): string | null {
        return that.queryParams[name];
      }

      getAll(name: string): string[] {
        return that.queryParams[name];
      }

      has(name: string): boolean {
        return !!that.queryParams[name];
      }
    })();
  }

  get routeConfig() {
    return this._routeConfig;
  }

  get firstChild(): ActivatedRouteSnapshot | null {
    return this._firstChild;
  }

  get pathFromRoot() {
    return this._pathFromRoot;
  }

  toString(): string {
    return 'bla';
  }
}
