import React from 'react';
import { render, fireEvent, act } from '@testing-library/react';
import * as suiteFrontend from '@theorchard/suite-frontend';
import { mockAppConfig } from 'lib/test-helpers/factories';
import { createIdentity } from 'lib/test-utils/identity-factory';
import { EVENT_HELP_CLICKED } from 'src/constants';
import IdentityContext from 'src/context';
import { services } from 'src/services/user';
import ZendeskButtonV2, { NO_ZENDESK_KEY_MSG } from '../zendesk-button-v2';

jest.mock('@theorchard/suite-frontend', () => ({
    ...jest.requireActual('@theorchard/suite-frontend'),
    useAppConfig: jest.fn(),
}));

const locale = 'LOCALE';
jest.mock('src/utils', () => ({
    getCurrentLocale: () => locale,
    formatMessage: (msg: string) => msg,
}));

const defaultConfig = {
    ...mockAppConfig,
    zendeskMessagingKey: 'thakey',
};

describe('<ZendeskButtonV2 />', () => {
    const zE = jest.fn();
    const zendeskAuthToken = 'ZENDESK_AUTH_TOKEN';

    const renderComponent = async ({
        configProps = {},
        zendeskToken = zendeskAuthToken as string | null,
    }: {
        configProps?: Record<string, unknown>;
        zendeskToken?: string | null;
    } = {}) => {
        const identity = createIdentity({ id: 1 });
        identity.identityId = 'IDENTITY_ID';

        jest.spyOn(suiteFrontend, 'useAppConfig').mockReturnValue({
            ...defaultConfig,
            ...configProps,
        });

        jest.spyOn(services, 'getGqlZendeskToken').mockResolvedValue(
            zendeskToken
        );

        const scriptMock = document.createElement('script');
        scriptMock.setAttribute('data-loaded', '');

        const originalGetElementById = document.getElementById.bind(document);
        jest.spyOn(document, 'getElementById').mockImplementation(elementId => {
            if (elementId === 'ze-snippet') return scriptMock;
            return originalGetElementById(elementId);
        });

        return render(
            <IdentityContext.Provider
                value={{ identity, hasAuthentication: () => false }}
            >
                <ZendeskButtonV2 />
            </IdentityContext.Provider>
        );
    };

    beforeEach(() => {
        window.zE = zE;
        zE.mockClear();
        jest.spyOn(suiteFrontend.Segment, 'trackEvent').mockImplementation(
            jest.fn()
        );
        jest.spyOn(console, 'warn').mockImplementation();
    });

    afterEach(() => {
        jest.restoreAllMocks();
    });

    describe('renders the component', () => {
        test('it renders correctly', async () => {
            const { container } = await renderComponent();
            const button = container.querySelector('#zendeskButton');

            expect(button).toBeInTheDocument();
            expect(button).toHaveClass('zE-Closed');
        });

        test("it won't render if theres no zendeskMessagingKey at the config", async () => {
            const { container } = await renderComponent({
                configProps: { zendeskMessagingKey: undefined },
            });
            const button = container.querySelector('#zendeskButton');

            expect(button).toBeNull();
            // eslint-disable-next-line no-console
            expect(console.warn).toHaveBeenCalledWith(NO_ZENDESK_KEY_MSG);
        });
    });

    describe('on load', () => {
        test('calls "close"', async () => {
            await renderComponent();

            expect(zE).toHaveBeenCalledWith('messenger', 'close');
        });

        test('set "conversationTags"', async () => {
            await renderComponent();

            expect(zE).toHaveBeenCalledWith(
                'messenger:set',
                'conversationTags',
                ['awal_beta_messaging']
            );
        });

        test('set "locale"', async () => {
            await renderComponent();

            expect(zE).toHaveBeenCalledWith('messenger:set', 'locale', locale);
        });

        test('login', async () => {
            await renderComponent();

            const callback = jest.fn();
            const [, , loginCallback] = zE.mock.calls.find(
                call => call[1] === 'loginUser'
            );
            loginCallback(callback);

            expect(callback).toHaveBeenCalledWith(zendeskAuthToken);
        });

        test('no login if no token', async () => {
            await renderComponent({
                zendeskToken: null,
            });

            const loginCall = zE.mock.calls.find(
                call => call[1] === 'loginUser'
            );
            expect(loginCall).not.toBeDefined();
            // eslint-disable-next-line no-console
            expect(console.warn).toHaveBeenCalledWith(
                'Can not get zendesk auth token'
            );
        });
    });

    describe('on click', () => {
        test('opens and closes on click', async () => {
            const { container } = await renderComponent();
            const button = container.querySelector('#zendeskButton');

            // opens on the first click
            if (button) fireEvent.click(button);
            const openCall = zE.mock.calls.find(call => call[1] === 'open');
            expect(openCall).toBeDefined();
            const [, , openCallback] = openCall;
            act(() => {
                openCallback();
            });

            // closes on the second click
            if (button) fireEvent.click(button);
            const closeCall = zE.mock.calls.find(call => call[1] === 'close');
            expect(closeCall).toBeDefined();
            const [, , closeCallback] = closeCall;
            act(() => {
                closeCallback();
            });
        });

        test('calls help action on click', async () => {
            const { container } = await renderComponent();
            const button = container.querySelector('#zendeskButton');

            if (button) fireEvent.click(button);
            const [, , callback] = zE.mock.calls.find(
                call => call[1] === 'open'
            );
            act(() => {
                callback();
            });

            expect(suiteFrontend.Segment.trackEvent).toHaveBeenCalledWith(
                EVENT_HELP_CLICKED
            );
        });
    });
});
