import React from 'react';
import { render, fireEvent, screen, act } from '@testing-library/react';
import * as suiteConfig from '@theorchard/suite-config';
import * as suiteIdentity from '@theorchard/suite-identity';
import { mockContext } from '../../../__mocks__';
import { EVENT_HELP_CLICKED } from '../../../constants/events';
import * as zendeskTokenQuery from '../../../queries/zendeskToken';
import Segment from '../../../segment';
import { ZendeskButtonV2 } from '../zendeskButtonV2';

const zendeskAuthToken = 'ZENDESK_AUTH_TOKEN';

const locale = 'no';

const mockConfig = {
    ...mockContext.config,
    zendeskMessagingKey: 'thakey',
};

const mockIdentity = mockContext.identity;

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

vi.mock('@theorchard/suite-i18n', () => ({
    getCurrentLocale: () => locale,
    formatMessage: (msg: string) => msg,
}));

vi.mock('../../../queries/zendeskToken');

describe('<ZendeskButtonV2>', () => {
    const zE = vi.fn();

    const renderComponent = ({
        configProps = {},
        identityProps = {},
        zendeskToken = { value: 'ZENDESK_AUTH_TOKEN' },
    }: {
        configProps?: Partial<suiteConfig.SuiteAppConfig>;
        identityProps?: Partial<suiteIdentity.Identity>;
        zendeskToken?: Partial<zendeskTokenQuery.ZendeskToken>;
    } = {}) => {
        const config = {
            ...mockConfig,
            ...configProps,
        };
        const identity = {
            ...mockIdentity,
            ...identityProps,
        };
        vi.spyOn(suiteConfig, 'useAppConfig').mockReturnValue(config as suiteConfig.SuiteAppConfig);
        vi.spyOn(suiteIdentity, 'useIdentity').mockReturnValue(identity as suiteIdentity.Identity);
        vi.spyOn(zendeskTokenQuery, 'useZendeskTokenQuery').mockReturnValue({
            loading: false,
            data: zendeskToken as zendeskTokenQuery.ZendeskToken,
        });

        const result = render(<ZendeskButtonV2 />);
        const zeScript = document.getElementById('ze-snippet');
        if (zeScript) fireEvent.load(zeScript);
        return result;
    };

    beforeEach(() => {
        window.zE = zE;
        zE.mockClear();
        vi.spyOn(Segment, 'trackEvent').mockImplementation(vi.fn());
    });

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

    test('renders component', () => {
        renderComponent();

        expect(screen.getByTestId('ZendeskButton')).toBeVisible();
    });

    test('appends script to document with zendesk key', () => {
        renderComponent();

        const script = screen.getByTestId('ZendeskScript');
        expect(script).toBeInTheDocument();
        expect(script.getAttribute('src')).toContain('thakey');
    });

    test('renders icon and text', () => {
        const { getByTestId, container } = renderComponent();

        expect(getByTestId('HelpNavIcon')).toBeVisible();
        expect(container.getElementsByClassName('NavLink-icon').item(0)).toBeInTheDocument();
        expect(container.getElementsByClassName('NavLink-title').item(0)).toBeInTheDocument();
        expect(screen.getByText('navigation_help')).toBeVisible();
    });

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

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

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

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

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

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

            const loginCall = zE.mock.calls.find((call) => call[1] === 'loginUser');
            expect(loginCall).not.toBeDefined();
        });
    });

    describe('on click', () => {
        test('opens and closes on click', () => {
            renderComponent();

            const button = screen.getByRole('button');

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

            // closes on second click
            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', () => {
            renderComponent();

            const button = screen.getByRole('button');
            fireEvent.click(button);

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

            expect(zE).toHaveBeenCalledWith('messenger:set', 'conversationTags', [
                'awal_beta_messaging',
                mockConfig.appName,
            ]);
            expect(zE).toHaveBeenCalledWith('messenger:set', 'locale', locale);
            expect(Segment.trackEvent).toHaveBeenCalledWith(EVENT_HELP_CLICKED);
        });

        test('set data on widget open', () => {
            renderComponent();

            const button = screen.getByRole('button');
            fireEvent.click(button);

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

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