import React from 'react';
import { render, fireEvent, getByTestId } from '@testing-library/react';
import { IdentityContext } from '@theorchard/suite-identity';
import { MemoryRouter } from 'react-router-dom';
import { mockContext } from '../../../__mocks__';
import { MainNavSectionOptions } from '../../../types';
import { MainNav, MainNavProps } from '../mainNav';

const app = { id: 'app1', name: 'AppONE', url: 'app1.com' };

const testContext = {
    ...mockContext,
    identity: {
        ...mockContext.identity,
        applications: [app],
    },
};

vi.mock('@theorchard/suite-auth', () => ({
    useAuthClient: () => ({}),
}));

vi.mock('@theorchard/suite-config', () => ({
    useAppConfig: () => mockContext.config,
}));

vi.mock('../../../app/appContext', () => ({
    useAppContext: () => testContext,
}));

describe('<MainNav>', () => {
    const item1 = { title: 'test1', path: 'test1' };
    const item2 = { title: 'test2', path: 'test2' };

    const section1 = { id: '1', title: 'section 1', items: [item1] };
    const section2 = { id: '2', title: 'section 2', items: [item2] };

    const defaultProps = {
        sections: [section1, section2],
    };

    const renderComponent = (props: MainNavProps = {}) =>
        render(<MainNav {...defaultProps} {...props} />, {
            wrapper: ({ children }) => (
                <IdentityContext.Provider value={{ identity: mockContext.identity }}>
                    <MemoryRouter>{children}</MemoryRouter>
                </IdentityContext.Provider>
            ),
        });

    test('renders', () => {
        const { container } = renderComponent();

        expect(container).toMatchSnapshot();
    });

    test('renders header', () => {
        const title = 'test-app';
        const { getByText, container } = renderComponent({ title });

        expect(getByText(title)).toBeVisible();
        expect(getByTestId(container, 'PlaceholderAppIcon')).toBeVisible();
    });

    test('renders menu sections and items', () => {
        const { getByText } = renderComponent();

        expect(getByText(section1.title)).toBeVisible();
        expect(getByText(section2.title)).toBeVisible();
        expect(getByText(item1.title)).toBeVisible();
        expect(getByText(item2.title)).toBeVisible();
    });

    test('renders default footer menu', () => {
        const { getByText } = renderComponent();

        expect(getByText('Mike Patton')).toBeVisible();
        expect(getByText('MP')).toBeVisible();
    });

    test('renders footer extra items', () => {
        const title = 'link to settings';
        const { container, getByText } = renderComponent({
            sections: [],
            footer: {
                items: [{ path: '/settings', title }],
            },
        });

        expect(container).toMatchSnapshot();
        expect(getByText(title)).toBeVisible();
        expect(getByText('Mike Patton')).toBeVisible();
        expect(getByText('MP')).toBeVisible();
    });

    describe('when "Applications" button is clicked', () => {
        test('renders apps in app switcher', async () => {
            const { getByText, findByTestId } = renderComponent();

            const element = getByText('Apps');
            fireEvent.click(element);

            const appBtn = await findByTestId('AppGridButton');
            expect(appBtn).toHaveAttribute('href', app.url);
            expect(appBtn).toHaveTextContent(app.name);
            expect(getByTestId(appBtn, 'PlaceholderAppIcon')).toBeVisible();
        });
    });

    describe('when there is a section with type "secondary"', () => {
        test('renders items in secondary section', () => {
            const sections: MainNavSectionOptions<unknown>[] = [
                { id: 'prim', type: 'primary', items: [{ title: 'Item 1', path: '/item1' }] },
                { id: 'sec', type: 'secondary', items: [{ title: 'Item 2', path: '/item2' }] },
            ];
            const { container } = renderComponent({ sections });

            const navSecMenuItems = container.querySelectorAll('.MainNav-menu-items');
            const secSection = navSecMenuItems.item(1);

            expect(navSecMenuItems).toHaveLength(2);
            expect(secSection).toHaveClass('secondary');
        });
    });

    describe('safe-ward against missing props', () => {
        const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(vi.fn());

        afterEach(() => {
            consoleErrorSpy.mockClear();
        });

        test('logs error when section is missing id', () => {
            const sections: MainNavSectionOptions<unknown>[] = [
                // @ts-expect-error | section missing id
                {
                    title: 'section 1',
                    items: [item1],
                },
            ];

            renderComponent({ sections });

            expect(consoleErrorSpy).toHaveBeenCalledWith(
                'The mainNav section (index: [0]) needs to specify an "id" field with a non empty value.'
            );
        });

        test('should log an error when an item has no valid "path"', () => {
            const sections: MainNavSectionOptions<unknown>[] = [
                {
                    id: '1',
                    type: 'primary',
                    items: [{ title: 'Item 1', path: '' }], // Invalid path
                },
            ];

            try {
                renderComponent({ sections });
            } catch (error) {
                // Ignore the error thrown by MainNavLink.
                // TODO: swap those for console.errors
            }

            expect(consoleErrorSpy).toHaveBeenCalledWith(
                'The mainNav section (id: "1") has an item (index: [0]) without a valid "path".'
            );
        });
    });
});
