import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { ErrorBoundary } from '@theorchard/suite-components';
import { formatMessage } from '@theorchard/suite-i18n';
import { MemoryRouter } from 'react-router-dom';
import { mockConsoleError } from '../../../../lib/test-utils/console';
import * as routeUtils from '../../../utils/routing';
import { MainNavLink, MainNavLinkProps } from '../mainNavLink';

describe('<MainNavLink>', () => {
    const onError = vi.fn();

    const renderComponent = (item: MainNavLinkProps, pathname = '/') =>
        render(<MainNavLink {...item} />, {
            wrapper: ({ children }) => (
                <ErrorBoundary onError={onError}>
                    <MemoryRouter initialEntries={[{ pathname }]}>{children}</MemoryRouter>
                </ErrorBoundary>
            ),
        });

    beforeEach(() => {
        onError.mockClear();
    });

    test('renders title and url', () => {
        const item = { path: '/somewhere', title: 'test' };
        const { getByText } = renderComponent(item);

        const element = getByText(item.title);
        expect(element).toBeVisible();
        expect(element.parentElement?.getAttribute('href')).toEqual(item.path);
    });

    test('throws if no "path" is provided', () => {
        mockConsoleError(() => {
            const item = { title: 'test' };
            const { getByText } = renderComponent(item);

            const element = getByText('Sorry, we hit a snag');
            expect(element).toBeVisible();
            expect(onError).toHaveBeenCalledTimes(1);
        });
    });

    describe('with "routeId"', () => {
        const route = {
            id: 'test-route',
            path: '/test-path',
            moduleName: 'test',
            page: () => <></>,
        };

        beforeEach(() => {
            vi.spyOn(routeUtils, 'getRoute').mockImplementation((id) =>
                id === route.id ? route : undefined
            );
        });

        test('renders url from matching routeId', () => {
            const item = { routeId: 'test-route', title: 'test' };
            const { getByText } = renderComponent(item);

            const element = getByText(item.title);
            expect(element).toBeVisible();
            expect(element.parentElement?.getAttribute('href')).toEqual(route.path);
        });

        test('throws if no matching routeId', () => {
            mockConsoleError(() => {
                const item = { routeId: 'no-route', title: 'test' };
                const { getByText } = renderComponent(item);

                const element = getByText('Sorry, we hit a snag');
                expect(element).toBeVisible();
                expect(onError).toHaveBeenCalledTimes(1);
            });
        });
    });

    test('renders translated term', () => {
        const item = {
            path: '/somewhere',
            title: 'test',
            term: 'account_navigation',
        };
        const { getByText, queryByText } = renderComponent(item);

        const element = getByText(formatMessage(item.term));
        expect(element).toBeVisible();

        const titleElement = queryByText(item.title);
        expect(titleElement).toBeNull();
    });

    test('does not add "selected" class if location equals path', () => {
        const item = { path: '/somewhere', title: 'test' };
        const { getByText } = renderComponent(item, item.path);

        const element = getByText(item.title);
        expect(element.parentElement).not.toHaveClass('NavLink-selected');
    });

    describe('with a "match" regex pattern like: "^\\/(test|item\\/\\d+)"', () => {
        const item = {
            path: '/somewhere',
            title: 'test',
            match: /^\/(test|item\/\d+)/,
        };

        test.each(['/test', '/item/12345'])(
            'adds "selected" class on location: "%s"',
            (pathname) => {
                const { getByText } = renderComponent(item, pathname);
                const element = getByText(item.title);
                expect(element.parentElement).toHaveClass('NavLink-selected');
            }
        );
    });

    describe('with "onClick" callback', () => {
        test('invokes callback when clicking on link', () => {
            const onClick = vi.fn();
            const testId = 'mylink';
            const { getByTestId } = renderComponent({
                onClick,
                testId,
                path: '/test',
            });

            const link = getByTestId(testId);
            fireEvent.click(link);
            expect(onClick).toHaveBeenCalledTimes(1);
        });
    });

    test('renders a navIcon', () => {
        const item: MainNavLinkProps = {
            path: '/about',
            title: 'About',
            term: 'about_navigation',
            navIcon: 'AboutNavIcon',
        };

        const { getByTestId } = renderComponent(item);

        const element = getByTestId(item.navIcon!);
        expect(element).toBeVisible();
    });

    test('renders an arbitrary icon passed as prop', () => {
        const item: MainNavLinkProps = {
            path: '/about',
            title: 'About',
            term: 'about_navigation',
            icon: <svg data-testid="iconProp">ImAnIcon</svg>,
        };

        const { getByTestId } = renderComponent(item);

        const element = getByTestId('iconProp');
        expect(element).toBeVisible();
    });
});
