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 * as suiteTheming from '@theorchard/suite-theming';
import { mockContext } from '../../../__mocks__';
import { EVENT_HELP_CLICKED } from '../../../constants/events';
import Segment from '../../../segment';
import { ZendeskButton, Props } from '../zendeskButton';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ZendeskCall = [target: string, action: string, arg?: any];

const locale = 'no';

const mockConfig = {
    ...mockContext.config,
    zendeskKey: '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,
}));

const getZendeskAuthCallbackMock = vi.fn();
vi.mock('../../../queries/zendeskToken', () => ({
    getZendeskAuthCallback: () => {
        return getZendeskAuthCallbackMock;
    },
}));

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

    const renderComponent = (
        configProps: Partial<suiteConfig.SuiteAppConfig> = {},
        props: Props = {},
        identityProps: Partial<suiteIdentity.Identity> = {}
    ) => {
        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);

        return render(<ZendeskButton {...props} />);
    };

    const simulateWidgetLoad = () => {
        const script = document.getElementById('ze-snippet');
        if (script) {
            act(() => {
                script.dispatchEvent(new Event('load'));
            });
        }
    };

    beforeEach(() => {
        window.zE = zE;
        zE.mockClear();

        vi.spyOn(Segment, 'trackEvent').mockImplementation(vi.fn());

        vi.spyOn(suiteTheming, 'useTheme').mockReturnValue({
            theme: 'awal',
        } as suiteTheming.ThemeContextType);
    });

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

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

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

    test('calls "identify" on the Zendesk api', () => {
        renderComponent();
        simulateWidgetLoad();

        expect(zE).toHaveBeenCalledWith('webWidget', 'identify', {
            name: mockIdentity.name,
            email: mockIdentity.email,
            organization: mockIdentity.defaultBrand,
        });
    });

    test('calls "updateSettings" for "webWidget:contactForm" on the Zendesk api with email prefill', () => {
        renderComponent();
        simulateWidgetLoad();

        expect(zE).toHaveBeenCalledWith('webWidget', 'updateSettings', {
            webWidget: {
                contactForm: {
                    fields: [{ id: 'email', prefill: { '*': mockIdentity.email } }],
                },
            },
        });
    });

    test('calls "updateSettings" for "webWidget:contactForm" on the Zendesk api with form id', () => {
        const zendeskTicketFormIds = '123';
        renderComponent({
            zendeskTicketFormIds: zendeskTicketFormIds,
        });
        simulateWidgetLoad();

        expect(zE).toHaveBeenCalledWith('webWidget', 'updateSettings', {
            webWidget: {
                contactForm: {
                    ticketForms: [{ id: zendeskTicketFormIds }],
                },
            },
        });
    });

    test('calls "updateSettings" for "webWidget:contactForm" on the Zendesk api with form ids', () => {
        const zendeskTicketFormIds = '123,456';
        renderComponent({
            zendeskTicketFormIds: zendeskTicketFormIds,
        });
        simulateWidgetLoad();

        expect(zE).toHaveBeenCalledWith('webWidget', 'updateSettings', {
            webWidget: {
                contactForm: {
                    ticketForms: [{ id: '123' }, { id: '456' }],
                },
            },
        });
    });

    test('calls "setLocale" on the Zendesk api', () => {
        renderComponent();
        simulateWidgetLoad();

        expect(zE).toHaveBeenCalledWith('webWidget', 'setLocale', locale);
    });

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

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

    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');
        callback();

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

    describe('applies brand widget colors on click', () => {
        const openCallbackAndGetExpectedCalls = (zECalls: ZendeskCall[]) => {
            const call = zECalls.find((call) => call[1] === 'open');
            if (call) {
                const [, , callback] = call;
                callback();
            }

            return zECalls
                .map((call) => {
                    const widgetColorCall =
                        call[0] === 'webWidget' &&
                        call[1] === 'updateSettings' &&
                        call[2].webWidget.color;
                    return widgetColorCall;
                })
                .filter(Boolean);
        };

        test('applies awal brand widget colors on click', () => {
            renderComponent();

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

            const expectedCalls = openCallbackAndGetExpectedCalls(zE.mock.calls);
            expect(expectedCalls).toHaveLength(1);
            expect(expectedCalls[0]).toEqual({
                articleLinks: '#3A1C40',
                button: '#7B3A88',
                header: '#3A1C40',
                resultLists: '#B069BF',
            });
        });

        test('applies orchard brand widget colors on click', () => {
            vi.spyOn(suiteTheming, 'useTheme').mockReturnValue({
                theme: 'orchard',
            } as suiteTheming.ThemeContextType);

            renderComponent();

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

            const expectedCalls = openCallbackAndGetExpectedCalls(zE.mock.calls);
            expect(expectedCalls[0]).toEqual({
                articleLinks: '#123249',
                button: '#276B9B',
                header: '#123249',
                resultLists: '#549FD4',
            });
        });
    });

    describe('extra "answerBot:suppress" and "helpCenter:setSuggestions" on click', () => {
        const openCallbackAndGetExpectedCalls = (zECalls: ZendeskCall[]) => {
            const call = zECalls.find((call) => call[1] === 'open');
            if (call) {
                const [, , callback] = call;
                callback();
            }

            return zECalls
                .map((call) => {
                    const botSuppressCall =
                        call[0] === 'webWidget' &&
                        call[1] === 'updateSettings' &&
                        call[2].webWidget.answerBot;

                    const setSuggestionsCall =
                        call[0] === 'webWidget' &&
                        call[1] === 'helpCenter:setSuggestions' &&
                        call[2].search;

                    return botSuppressCall || setSuggestionsCall;
                })
                .filter(Boolean);
        };

        test('should not be called for ORCHARD', () => {
            renderComponent();

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

            const expectedCalls = openCallbackAndGetExpectedCalls(zE.mock.calls);
            expect(expectedCalls).toHaveLength(0);
        });

        test('should be called for AWAL on the SettingsApp', () => {
            renderComponent({
                brand: 'awal',
                appName: 'frontend-settings',
            });

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

            const expectedCalls = openCallbackAndGetExpectedCalls(zE.mock.calls);
            expect(expectedCalls).toHaveLength(2);
            expect(expectedCalls[0]).toEqual({ suppress: true });
            expect(expectedCalls[1]).toEqual('settings');
        });

        test('should be called for AWAL on the InsightsApp', () => {
            renderComponent({
                brand: 'awal',
                appName: 'frontend-insights',
            });

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

            const expectedCalls = openCallbackAndGetExpectedCalls(zE.mock.calls);
            expect(expectedCalls).toHaveLength(2);
            expect(expectedCalls[0]).toEqual({ suppress: true });
            expect(expectedCalls[1]).toEqual('insights');
        });
    });

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

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

    test('renders icon and text with prop asNavLink=true', () => {
        const { getByTestId, container } = renderComponent({}, { asNavLink: true });

        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('JWT auth', () => {
        const filterTokenCalls = (zECalls: ZendeskCall[]) => {
            return zECalls
                .map((call) => {
                    return (
                        call[0] === 'webWidget' &&
                        call[1] === 'updateSettings' &&
                        call[2]?.webWidget?.authenticate?.jwtFn === getZendeskAuthCallbackMock
                    );
                })
                .filter(Boolean);
        };

        test('no token auth for orchard', () => {
            renderComponent();

            const tokenCalls = filterTokenCalls(zE.mock.calls);
            expect(tokenCalls).toHaveLength(0);
        });

        test('token auth for AWAL', () => {
            renderComponent({ brand: 'awal' }, {}, {});
            simulateWidgetLoad();

            const tokenCalls = filterTokenCalls(zE.mock.calls);
            expect(tokenCalls).toHaveLength(1);
            expect(zE).toHaveBeenCalledWith('webWidget', 'updateSettings', {
                webWidget: {
                    authenticate: {
                        jwtFn: getZendeskAuthCallbackMock,
                    },
                },
            });
        });
    });
});
