import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { AuthClient } from '@theorchard/suite-auth';
import { formatMessage } from '@theorchard/suite-i18n';
import { ThemeProvider } from '@theorchard/suite-theming';
import * as storageUtils from '../../../utils/storage';
import { ErrorPage, Props } from '../errorPage';

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

describe('<ErrorPage>', () => {
    const error = {
        name: 'AuthError',
        message: 'TEST',
    };

    beforeEach(() => {
        mockEnvironment = 'prod';
    });

    const createDefaultProps = () => {
        const location = {
            search: '?state=1234&code=2345&error=oops',
        } as Location;
        const history = {} as History;

        return { location, history };
    };

    const renderWrapper = (props: Partial<Props> = {}) => {
        const { location, history } = createDefaultProps();
        return render(<ErrorPage error={error} location={location} history={history} {...props} />);
    };

    test('has classname', () => {
        const { container } = renderWrapper();
        const element = container.getElementsByClassName('ErrorPage').item(0);
        expect(element).toBeVisible();
    });

    test('shows error message', () => {
        const { getByText } = renderWrapper({});
        const element = getByText(formatMessage('error_generic_title'));
        expect(element).toBeVisible();
    });

    test('shows error illustration', () => {
        const { getByTestId } = renderWrapper({});
        const element = getByTestId('ErrorIllustration');
        expect(element).toBeVisible();
    });

    test('does not shows error details in "prod" environment', () => {
        const { queryByText } = renderWrapper({});
        const element = queryByText(error.message);
        expect(element).toBeNull();
    });

    test.each(['qa', 'dev'])('shows error details in "%s" environment', (env) => {
        mockEnvironment = env;
        const { getByText } = renderWrapper({});
        const element = getByText(error.message);
        expect(element).toBeVisible();
    });

    describe('with client', () => {
        test('shows logout button', () => {
            const client = { logout: vi.fn() } as unknown as AuthClient;
            const { getByText } = renderWrapper({ client });
            const element = getByText(formatMessage('error_backToLogin'));
            expect(element).toBeVisible();
        });
    });

    describe('logout button clicked', () => {
        test('calls logout on client', () => {
            const client = { logout: vi.fn() } as unknown as AuthClient;
            const { getByText } = renderWrapper({ client });
            const element = getByText(formatMessage('error_backToLogin'));
            element.click();
            expect(client.logout).toHaveBeenCalled();
        });
    });

    describe('retry button clicked', () => {
        test('calls logout on client', () => {
            const clearStorage = vi.spyOn(storageUtils, 'clearAppStorage').mockReturnValue();
            const location = {
                reload: vi.fn(),
                search: '',
            } as unknown as Location;
            const history = { replaceState: vi.fn() } as unknown as History;
            const { getByText } = render(
                <ErrorPage error={error} location={location} history={history} />
            );

            expect(location.reload).not.toHaveBeenCalled();

            const retryButton = getByText(formatMessage('error_retry'));
            fireEvent.click(retryButton);

            expect(clearStorage).toHaveBeenCalled();
            expect(history.replaceState).toHaveBeenCalled();
            expect(location.reload).toHaveBeenCalled();
        });
    });

    describe('on invalid state error', () => {
        test('does not call retry with error code in url', () => {
            const location = {
                reload: vi.fn(),
                search: '?state=1234&code=2345&error=oops',
            } as unknown as Location;
            const history = { replaceState: vi.fn() } as unknown as History;
            render(<ErrorPage error={error} location={location} history={history} />);

            expect(location.reload).not.toHaveBeenCalled();
        });
    });

    describe('with V2 color scheme', () => {
        test('renders "error" illustration', () => {
            const { location, history } = createDefaultProps();
            const { getByTestId } = render(
                <ErrorPage error={error} location={location} history={history} />,
                {
                    wrapper: ({ children }) => (
                        <ThemeProvider config={{ appName: 'test', cdnUrl: 'test.com' }}>
                            {children}
                        </ThemeProvider>
                    ),
                }
            );

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