// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { NavController as NavControllerService } from '@ionic/angular';

import { mock, mockSingleton } from '@/testing/utils';

import { CoreNavigatorService } from '@services/navigator';
import { CoreUtils, CoreUtilsProvider } from '@services/utils/utils';
import { CoreUrlUtils, CoreUrlUtilsProvider } from '@services/utils/url';
import { CoreTextUtils, CoreTextUtilsProvider } from '@services/utils/text';
import { NavController, Router } from '@singletons';
import { ActivatedRoute, RouterState } from '@angular/router';
import { CoreSites } from '@services/sites';
import { CoreMainMenu } from '@features/mainmenu/services/mainmenu';

describe('CoreNavigator', () => {

    let router: {
        url?: string;
        routerState?: Partial<RouterState>;
    };
    let currentMainMenuHandlers: string[];
    let navigator: CoreNavigatorService;
    let navControllerMock: NavControllerService;

    beforeEach(() => {
        router = { url: '/' };
        currentMainMenuHandlers = ['home'];
        navigator = new CoreNavigatorService();
        navControllerMock = mockSingleton(NavController, ['navigateRoot', 'navigateForward']);

        mockSingleton(Router, router);
        mockSingleton(CoreUtils, new CoreUtilsProvider(mock()));
        mockSingleton(CoreUrlUtils, new CoreUrlUtilsProvider());
        mockSingleton(CoreTextUtils, new CoreTextUtilsProvider(mock()));
        mockSingleton(CoreSites, { getCurrentSiteId: () => 42, isLoggedIn: () => true });
        mockSingleton(CoreMainMenu, { isMainMenuTab: path => Promise.resolve(currentMainMenuHandlers.includes(path)) });
    });

    it('matches against current path', () => {
        router.url = '/main/foo';

        expect(navigator.isCurrent('/main/foo')).toBe(true);
        expect(navigator.isCurrent('/main')).toBe(false);
    });

    it('gets the current main menu tab', () => {
        expect(navigator.getCurrentMainMenuTab()).toBeNull();

        router.url = '/main/foo';
        expect(navigator.getCurrentMainMenuTab()).toBe('foo');

        router.url = '/main/foo/bar';
        expect(navigator.getCurrentMainMenuTab()).toBe('foo');
    });

    it('navigates to absolute paths', async () => {
        const success = await navigator.navigate('/main/foo/bar', { reset: true });

        expect(success).toBe(true);
        expect(navControllerMock.navigateRoot).toHaveBeenCalledWith(['/main/foo/bar'], {});
    });

    it('navigates to relative paths', async () => {
        // Arrange.
        const mainOutletRoute = { routeConfig: { path: 'foo' } };
        const primaryOutletRoute = { routeConfig: { path: 'main' }, firstChild: mainOutletRoute };
        const rootRoute = { firstChild: primaryOutletRoute };

        router.routerState = { root: rootRoute as unknown as ActivatedRoute };

        // Act.
        const success = await navigator.navigate('./bar');

        // Assert.
        expect(success).toBe(true);
        expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['./bar'], { relativeTo: mainOutletRoute });
    });

    it('navigates to site paths', async () => {
        // Arrange
        router.url = '/main/foo';

        // Act
        const success = await navigator.navigateToSitePath('/user/42');

        // Assert
        expect(success).toBe(true);
        expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/foo/user/42'], {});
    });

    it('navigates to site paths using tabs', async () => {
        // Arrange
        currentMainMenuHandlers.push('users');

        // Act
        const success = await navigator.navigateToSitePath('/users/user/42');

        // Assert
        expect(success).toBe(true);
        expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/users/user/42'], {});
    });

    it('navigates to site paths using the default tab', async () => {
        const success = await navigator.navigateToSitePath('/user/42');

        expect(success).toBe(true);
        expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/home'], {
            queryParams: {
                redirectPath: '/main/home/user/42',
            },
        });
    });

    it('navigates to site paths ussing different path formats', async () => {
        currentMainMenuHandlers.push('users');

        const assertNavigation = async (currentPath, sitePath, expectedPath) => {
            router.url = currentPath;

            const success = await navigator.navigateToSitePath(sitePath);

            expect(success).toBe(true);
            expect(navControllerMock.navigateForward).toHaveBeenCalledWith([expectedPath], {});
        };

        await assertNavigation('/main/users', '/main/users/user/42', '/main/users/user/42');
        await assertNavigation('/main/users', '/users/user/42', '/main/users/user/42');
        await assertNavigation('/main/users', '/user/42', '/main/users/user/42');
        await assertNavigation('/main/home', '/users/user/42', '/main/users/user/42');
    });

    it('navigates to site home', async () => {
        const success = await navigator.navigateToSiteHome();

        expect(success).toBe(true);
        expect(navControllerMock.navigateRoot).toHaveBeenCalledWith(['/main/home'], {});
    });

    it.todo('navigates to a different site');
    it.todo('navigates to login credentials');
    it.todo('navigates to NO_SITE_ID site');

});
