import React from 'react';
import { SuiteAppConfig } from '@theorchard/suite-config';
import { Identity } from '@theorchard/suite-identity';
import { GraphqlClient, SuiteAuthClient } from '../../types';
import {
    clearRoutes,
    getModuleRoutes,
    getRoutes,
    hasAccessToRoutes,
    registerModuleRoute,
    registerRootRoutes,
} from '../routing';

beforeEach(() => {
    clearRoutes();
});

const Noop = () => <></>;

describe('registerRootRoutes', () => {
    const route1 = { path: '/home', order: 0, page: Noop };
    const route2 = { path: '/about', order: 0, page: Noop };

    test('registers root routes', () => {
        registerRootRoutes([route1, route2]);

        expect(getRoutes()).toEqual([route1, route2]);
    });
});

describe('getRoutes', () => {
    test('returns routes sorted by given sequence, root first', () => {
        registerModuleRoute('one', { path: '/one', page: Noop });
        registerModuleRoute('two', { path: '/two', page: Noop });
        registerRootRoutes([{ path: '/zero', page: Noop }]);

        expect(getRoutes()).toMatchInlineSnapshot(`
            [
              {
                "page": [Function],
                "path": "/zero",
              },
              {
                "moduleName": "one",
                "page": [Function],
                "path": "/one",
              },
              {
                "moduleName": "two",
                "page": [Function],
                "path": "/two",
              },
            ]
        `);
    });

    test('returns routes sorted by order', () => {
        registerModuleRoute('one', { path: '/one', order: 1, page: Noop });
        registerModuleRoute('two', { path: '/two', order: 0, page: Noop });
        registerRootRoutes([{ path: '/root', page: Noop }]);

        expect(getRoutes()).toMatchInlineSnapshot(`
            [
              {
                "moduleName": "two",
                "order": 0,
                "page": [Function],
                "path": "/two",
              },
              {
                "moduleName": "one",
                "order": 1,
                "page": [Function],
                "path": "/one",
              },
              {
                "page": [Function],
                "path": "/root",
              },
            ]
        `);
    });

    test('accepts a module filter', () => {
        registerModuleRoute('one', { path: '/one', order: 1, page: Noop });
        registerModuleRoute('two', { path: '/two', order: 0, page: Noop });
        registerRootRoutes([{ path: '/root', order: 2, page: Noop }]);

        expect(getRoutes('two')).toMatchInlineSnapshot(`
            [
              {
                "moduleName": "two",
                "order": 0,
                "page": [Function],
                "path": "/two",
              },
              {
                "order": 2,
                "page": [Function],
                "path": "/root",
              },
            ]
        `);
    });
});

describe('registerModuleRoute', () => {
    test('registers a module route', () => {
        const moduleName = 'my-module';
        const routeOne = { path: '/one', order: 1, page: Noop };
        const routeTwo = { path: '/two', order: 1, page: Noop };

        registerModuleRoute(moduleName, routeOne);
        registerModuleRoute(moduleName, routeTwo);

        expect(getRoutes(moduleName)).toEqual([
            { ...routeOne, moduleName },
            { ...routeTwo, moduleName },
        ]);
    });
});

describe('hasAccessToModuleRoutes', () => {
    const config = {} as SuiteAppConfig;
    const identity = {} as Identity;
    const graphqlClient = {} as GraphqlClient;
    const authClient = {} as SuiteAuthClient;

    const createContext = (roles: string[]) => ({
        config,
        identity: { ...identity, roles },
        graphqlClient,
        authClient,
    });

    const page1 = () => <>home</>;
    const page2 = () => <>about</>;

    beforeEach(() => {
        registerModuleRoute('one', {
            id: 'home',
            path: '/home',
            page: page1,
            roles: ['view'],
        });

        registerModuleRoute('two', {
            id: 'admin',
            path: '/admin',
            page: page2,
            roles: ['admin'],
        });
    });

    test('returns true if user has access to one or more routes', () => {
        const fn = hasAccessToRoutes(['home', 'admin']);

        expect(fn(createContext(['view']))).toEqual(true);
        expect(fn(createContext(['admin']))).toEqual(true);
        expect(fn(createContext(['view', 'admin', 'other']))).toEqual(true);
    });

    test('returns false if user does not have access to one or more routes', () => {
        const fn = hasAccessToRoutes(['home', 'admin']);

        expect(fn(createContext(['other']))).toEqual(false);
        expect(fn(createContext([]))).toEqual(false);
    });

    test('returns false if the route does not exist', () => {
        const fn = hasAccessToRoutes(['other']);

        expect(fn(createContext(['other']))).toEqual(false);
    });
});

describe('getModuleRoutes', () => {
    test('returns routes registered by modules only', () => {
        registerModuleRoute('one', { path: '/home', order: 1, page: Noop });
        registerModuleRoute('two', { path: '/home', page: Noop });
        registerRootRoutes([{ path: '/home', order: 0, page: Noop }]);

        expect(getModuleRoutes()).toEqual([
            { path: '/home', order: 1, page: Noop, moduleName: 'one' },
            { path: '/home', page: Noop, moduleName: 'two' },
        ]);
    });
});
