import React from 'react';
import { render as renderDom } from '@testing-library/react';
import { AppConfigContext, SuiteAppConfig } from '@theorchard/suite-config';
import { MemoryRouter } from 'react-router-dom';
import * as appContext from '../../../app/appContext';
import Segment from '../../../segment';
import { AppContext } from '../../../types';
import * as routeUtils from '../../../utils/routing';
import { ModuleRouterOutlet } from '../moduleRouterOutlet';

describe('<ModuleRouterOutlet>', () => {
    const context = {
        graphqlClient: {},
        identity: { features: {} },
        config: {},
        authClient: {},
    } as AppContext;

    const render = (pathname = '/') =>
        renderDom(<ModuleRouterOutlet />, {
            wrapper: ({ children }) => (
                <AppConfigContext.Provider
                    value={{ config: { environment: 'dev' } as SuiteAppConfig }}
                >
                    <MemoryRouter initialEntries={[{ pathname }]}>{children}</MemoryRouter>
                </AppConfigContext.Provider>
            ),
        });

    beforeEach(() => {
        const moduleName = 'module-one';

        vi.spyOn(appContext, 'useAppContext').mockReturnValue(context);

        vi.spyOn(routeUtils, 'getModuleRoutes').mockReturnValue([
            {
                path: '/page-one',
                className: 'page-one',
                moduleName,
                exact: false,
                page: () => <>PAGE_ONE</>,
            },
            {
                path: '/page-exact',
                exact: true,
                moduleName,
                page: () => <>PAGE_EXACT</>,
            },
            {
                path: '/with-role',
                moduleName,
                page: () => <>ROLE_PROTECTED</>,
                roles: ['test-role'],
            },
            {
                path: '/with-feature',
                moduleName,
                page: () => <>FEATURE_PROTECTED</>,
                featureFlags: ['test-feature'],
            },
            {
                path: '/boom',
                moduleName,
                page: () => {
                    throw new Error('BOOM');
                },
            },
        ]);
    });

    test('applies route "className"', () => {
        const { getByText } = render('/page-one');
        const pageElement = getByText('PAGE_ONE');

        expect(pageElement).toHaveClass('page-one');
    });

    test('errors are handled', () => {
        /* eslint-disable no-console */
        const consoleError = console.error;
        console.error = vi.fn();

        const { getByText } = render('/boom');
        const errorElement = getByText('BOOM');

        expect(errorElement).toHaveClass('ErrorPage-details');

        console.error = consoleError;
        /* eslint-enable no-console */
    });

    test('page views are tracked once', () => {
        vi.spyOn(Segment, 'trackPage');

        const { rerender } = render('/page-one');
        rerender(<ModuleRouterOutlet />);
        rerender(<ModuleRouterOutlet />);

        expect(Segment.trackPage).toHaveBeenCalledTimes(1);
    });

    describe('with feature flagged route', () => {
        test('identity has feature; renders page', () => {
            vi.spyOn(appContext, 'useAppContext').mockReturnValue({
                ...context,
                identity: {
                    ...context.identity,
                    features: { 'test-feature': true },
                },
            });

            const { getByText } = render('/with-feature');

            expect(getByText('FEATURE_PROTECTED')).toBeInTheDocument();
        });

        test('identity does not have feature; does not render page', () => {
            vi.spyOn(appContext, 'useAppContext').mockReturnValue({
                ...context,
                identity: {
                    ...context.identity,
                    features: {},
                },
            });

            const { queryByText } = render('/with-feature');

            expect(queryByText('FEATURE_PROTECTED')).toBeNull();
        });
    });

    describe('with role protected route', () => {
        test('identity has role; renders page', () => {
            vi.spyOn(appContext, 'useAppContext').mockReturnValue({
                ...context,
                identity: {
                    ...context.identity,
                    roles: ['test-role'],
                },
            });

            const { getByText } = render('/with-role');

            expect(getByText('ROLE_PROTECTED')).toBeInTheDocument();
        });

        test('identity does not have role; does not render page', () => {
            vi.spyOn(appContext, 'useAppContext').mockReturnValue({
                ...context,
                identity: {
                    ...context.identity,
                    roles: [],
                },
            });

            const { queryByText } = render('/with-role');

            expect(queryByText('ROLE_PROTECTED')).toBeNull();
        });
    });

    describe('without the "exact" flag', () => {
        test('renders page on sub paths', () => {
            const { getByText } = render('/page-one/sub');

            expect(getByText('PAGE_ONE')).toBeInTheDocument();
        });

        test('render page on exact path', () => {
            const { getByText } = render('/page-one');

            expect(getByText('PAGE_ONE')).toBeInTheDocument();
        });
    });

    describe('with the "exact" flag', () => {
        test('render page on exact path', () => {
            const { getByText } = render('/page-exact');

            expect(getByText('PAGE_EXACT')).toBeInTheDocument();
        });

        test('does not render page on sub paths', () => {
            const { queryByText } = render('/page-exact/sub');

            expect(queryByText('PAGE_EXACT')).toBeNull();
        });
    });
});
