import React from 'react';
import { ApolloClient, DocumentNode, NormalizedCacheObject } from '@apollo/client';
import { shallow, ShallowWrapper } from 'enzyme';
import { get } from 'lodash-es';
import { act } from 'react-dom/test-utils';
import { STORE, IDENTICAL_ARTISTS_ERR_MESSAGE } from '../../../constants';
import { LabelParticipantsByProductQueryGql } from '../../../queries/labelParticipantByProduct';
import { LabelParticipantByProjectGql } from '../../../queries/labelParticipantByProject';
import { ParticipantAppleMusicSearchGql } from '../../../queries/participantAppleSearch';
import { ParticipantSpotifyArtistGql } from '../../../queries/participantSpotifyArtist';
import { ParticipantSpotifySearchGql } from '../../../queries/participantSpotifySearch';
import { TrackParticipantsByProductQueryGql } from '../../../queries/trackParticipantsByProduct';
import * as analytics from '../../../utils/analytics';
import { pagesContext } from '../../addNewArtist/__fixtures__/search';
import ArtistDropdown, { Props, OnChangeValue } from '../artistDropdown';
import ArtistDropdownCompoundMessage from '../artistDropdownCompoundMessage';
import ArtistDropdownInfoMessage from '../artistDropdownInfoMessage';

const waitFor = async (amount = 0) => await new Promise((resolve) => setTimeout(resolve, amount));

const wait = async (amount = 0) => {
    // eslint-disable-next-line testing-library/no-unnecessary-act
    await act(async () => {
        await waitFor(amount);
    });
};

describe('<ArtistDropdown>', () => {
    const graphqlResponse = {
        data: {
            artistsSearch: {
                artists: [
                    { artistId: '1', artistName: 'Artist 1' },
                    { artistId: '2', artistName: 'Artist 2' },
                ],
            },
        },
    };
    const neo4jGraphqlResponse = {
        data: {
            orchardLabel: {
                participantSearchV2: {
                    items: [
                        {
                            item: {
                                id: '1',
                                name: 'Artist 1',
                                appleMusicId: 'apple_ID',
                            },
                        },
                        {
                            item: {
                                id: '2',
                                name: 'Artist 2',
                                spotifyId: 'spotify_ID',
                            },
                        },
                    ],
                },
            },
        },
    };
    const graphqlSpotifyResponse = {
        data: {
            participantServiceSearch: { serviceArtists: pagesContext[0].data },
        },
    };
    const graphqlAppleMusicResponse = {
        data: {
            participantServiceSearch: { serviceArtists: pagesContext[1].data },
        },
    };
    const graphqlSpotifyArtistResponse = {
        data: { participantServiceArtist: pagesContext[0].data[0] },
    };
    const graphqlAppleMusicArtistResponse = {
        data: { participantServiceArtist: pagesContext[1].data[0] },
    };
    const createLabelParticipantResponse = {
        data: {
            createLabelParticipant: {
                id: '503',
                name: 'Test',
                appleMusicId: 'apple_ID',
                spotifyId: 'spotify_ID',
            },
        },
    };
    const createLabelParticipantWithArtistInfoResponse = {
        data: {
            createLabelParticipant: {
                id: '503',
                name: 'Test',
                appleMusicId: 'apple_ID',
                spotifyId: 'spotify_ID',
                artistInfo: [{ id: '500' }],
            },
        },
    };
    const saveArtistResponse = {
        data: {
            saveArtists: [{ artistId: '500', artistName: 'Test' }],
        },
    };
    const saveArtistsResponse = {
        data: {
            saveArtists: [
                { artistId: '500', artistName: 'Test1' },
                { artistId: '501', artistName: 'Test2' },
            ],
        },
    };
    const graphqlIncorrectArtistResponse = {
        data: { labelParticipantIncorrectProfile: true },
    };
    const options = [
        {
            label: 'Artist 1',
            value: 'Artist 1',
            data: { name: 'Artist 1', id: 1 },
        },
        {
            label: 'Artist 2',
            value: 'Artist 2',
            data: { name: 'Artist 2', id: 2 },
        },
    ];
    const optionsWithVariousArtist = [
        {
            label: 'Artist 1',
            value: 'Artist 1',
            data: { name: 'Artist 1', id: 1 },
        },
        {
            label: 'Various Artists',
            value: 'Various Artists',
            data: { name: 'Various Artists' },
        },
    ];
    const selectedProfile = {
        label: 'test',
        value: '1',
        data: {
            name: 'test',
            appleMusicId: 'apple_ID',
            spotifyId: 'spotify_ID',
        },
    };
    const vendorProps = { vendorId: 42, subaccountId: 100 };

    const createClient = (props?: object) =>
        ({
            query: jest.fn(),
            mutate: jest.fn(),
            ...props,
        }) as unknown as ApolloClient<NormalizedCacheObject>;
    const getName = ({ definitions }: DocumentNode) => get(definitions[0], 'name.value');
    const renderComponent = (props?: Partial<Props>) =>
        shallow(<ArtistDropdown client={createClient()} {...props} />);
    const getInstance = (component: ShallowWrapper) => component.instance() as ArtistDropdown;

    describe('when component did mount and productId and trackId is provided', () => {
        it('calls trackParticipantsByProduct query and handles null results', async () => {
            const client = createClient({
                query: jest.fn(
                    async () =>
                        await Promise.resolve({
                            data: { product: { tracks: [null] } },
                        })
                ),
            });
            const testProps = { productId: '123', trackId: '234', client };
            renderComponent(testProps);
            await wait(1);
            expect(client.query).toHaveBeenCalledWith({
                query: TrackParticipantsByProductQueryGql,
                variables: { productId: '123' },
                fetchPolicy: 'network-only',
            });
        });
    });

    describe('when it renders', () => {
        it('displays default layout', () => {
            expect(renderComponent()).toMatchSnapshot();
        });
        it('displays default SingleSelect layout', () => {
            expect(renderComponent({ multi: false })).toMatchSnapshot();
        });
        it('displays compound layout', () => {
            const component = renderComponent();
            component.setState({
                compoundArtist: true,
                isCreateInProgress: true,
            });
            expect(component.find(ArtistDropdownCompoundMessage).exists()).toBeTruthy();
        });
        it('displays compound message', () => {
            const compoundMessageArtist = {
                compoundArtist: { artists: ['Test1', 'Test2'] },
            };
            const component = renderComponent();
            component.setState({ compoundMessageArtist });
            expect(component.find(ArtistDropdownInfoMessage)).toMatchSnapshot();
        });
        it('displays various artists layout', () => {
            const component = renderComponent();
            component.setState({ variousArtist: true });
            expect(component.find(ArtistDropdownInfoMessage).exists()).toBeTruthy();
        });
        it('does not displays various artists layout when featuring mode', () => {
            const component = renderComponent({
                participationRole: 'featuring',
            });
            component.setState({ variousArtist: true });
            expect(component.find(ArtistDropdownInfoMessage).exists()).toBe(false);
        });
        it('displays various artists message', () => {
            const component = renderComponent();
            component.setState({ variousArtistMessage: true });
            expect(component.find(ArtistDropdownInfoMessage).exists()).toBeTruthy();
        });
        it('displays placeholder text', () => {
            expect(
                renderComponent({ placeholder: 'Some custom placeholder text' })
            ).toMatchSnapshot();
        });

        it('displays error-correction track artists', () => {
            const testProps = {
                productId: '123',
                trackId: '321',
                isCorrectionMode: true,
                participationRole: 'performer',
                artistsInCorrectionMode: [
                    {
                        keyId: '321',
                        artistName: 'Test1',
                        fieldName: 'performer',
                        keyValue: [{ name: 'Test1', type: 'performer' }],
                    },
                ],
            };
            const participants = [{ name: 'Test1', isCorrectionMode: true }];
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            expect(instance.state.participants).toEqual(participants);
            expect(component).toMatchSnapshot();
        });
        it('not displays the AddNewArtist component', () => {
            const client = createClient({
                query: jest.fn(async () => await Promise.resolve()),
            });
            const testProps = {
                client,
                productId: '123',
                participationRole: 'performer',
                errorMessage: IDENTICAL_ARTISTS_ERR_MESSAGE,
                vendorId: 123,
                subaccountId: 456,
                isIdenticalArtistClearOutEnabled: true,
            };
            const component = renderComponent(testProps);
            component.setState({ updateArtistProfiles: true });
            expect(component.find('AddNewArtist').exists()).toBeFalsy();
        });
    });
    describe('with handlers', () => {
        let client: ApolloClient<NormalizedCacheObject>;

        beforeEach(() => {
            client = createClient({
                query: jest.fn(async () => await Promise.resolve(graphqlResponse)),
                mutate: jest.fn(async () => await Promise.resolve(saveArtistResponse)),
            });
        });

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

        it('calls formatCreateLabel and returns correct value', () => {
            const instance = getInstance(renderComponent());
            expect(instance.formatCreateLabel('Test')).toBe('+ Create New Artist "Test"');
        });
        it('calls handleFetch and fetches unique artists', async () => {
            const testTerm = 'Test';
            const testProps = { ...vendorProps, client };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleFetch(testTerm).then((result) => {
                expect(client.query).toHaveBeenCalled();
                expect(result).toEqual([
                    { artistInfoId: '1', name: 'Artist 1' },
                    { artistInfoId: '2', name: 'Artist 2' },
                ]);
            });
        });
        it('calls handleFetch and does not fetch unique artists if query search is empty', async () => {
            const testTerm = '';
            const testProps = { ...vendorProps, client };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleFetch(testTerm).then((result) => {
                expect(result).toEqual([]);
                expect(client.query).toHaveBeenCalledTimes(0);
            });
        });
        it('calls handleFetch and fetches using custom handler', async () => {
            const testProps = {
                ...vendorProps,
                client,
                onFetch: jest.fn(async () => await Promise.resolve([])),
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleFetch('Test').then(() => {
                expect(client.query).toHaveBeenCalledTimes(0);
                expect(testProps.onFetch).toHaveBeenCalledWith(
                    'Test',
                    vendorProps.vendorId,
                    vendorProps.subaccountId
                );
            });
        });
        it('calls handleFetch and returns VA option if request matches "Various Artists"', async () => {
            const testTerm = '  various artists';
            const testProps = { ...vendorProps, client };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleFetch(testTerm).then((result) => {
                expect(client.query).toHaveBeenCalledTimes(0);
                expect(result).toEqual([{ name: 'Various Artists' }]);
            });
        });
        it('calls handleChange and does nothing if value is empty', () => {
            const instance = getInstance(renderComponent());
            expect(instance.handleChange()).toBeUndefined();
        });
        it('calls handleChange and triggers onChange if value is a single item', () => {
            const onChangeData = [options[0].data];
            const testProps = { onChange: jest.fn() };
            const instance = getInstance(renderComponent(testProps));
            instance.handleChange(options[0]);
            expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
        });
        it('calls handleChange and triggers onChange if value is an array', () => {
            const onChangeData = options.map(({ data }) => data);
            const testProps = { onChange: jest.fn() };
            const instance = getInstance(renderComponent(testProps));
            instance.handleChange(options);
            expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
        });
        it('calls handleChange and triggers create LP if value is VA', () => {
            const onChangeData = optionsWithVariousArtist.map(({ data }) => data);
            const testProps = {
                client,
                onChange: jest.fn(),
                isUpdateArtistMode: true,
            };
            const instance = getInstance(renderComponent(testProps));
            instance.handleChange(optionsWithVariousArtist);
            expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
            expect(testProps.client.mutate).toHaveBeenCalled();
        });
        it('calls createArtists and creates artist successfully', async () => {
            const newArtistName = 'Test';
            const artistInfoId = saveArtistResponse.data.saveArtists[0].artistId;
            const onChangeData = [
                { artistInfoId: 1, name: 'abc' },
                { artistInfoId: 2, name: 'def' },
                { artistInfoId: 3, name: 'ghi' },
                { artistInfoId, name: newArtistName },
            ];
            const testProps = {
                client,
                onChange: jest.fn(),
                value: [
                    { artistInfoId: 1, name: 'abc' },
                    { artistInfoId: 2, name: 'def' },
                    { artistInfoId: 3, name: 'ghi' },
                ],
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.createArtists([newArtistName]).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalled();
                expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
                expect(component.find(ArtistDropdownInfoMessage).exists()).toBeTruthy();
            });
        });
        it('calls createArtists and creates several artists successfully', async () => {
            const newArtistName = ['Test1', 'Test2'];
            const onChangeData = [
                { artistInfoId: 1, name: 'abc' },
                { artistInfoId: 2, name: 'def' },
                { artistInfoId: '500', name: newArtistName[0] },
                { artistInfoId: '501', name: newArtistName[1] },
            ];
            const testProps = {
                client: createClient({
                    ...client,
                    mutate: jest.fn(async () => await Promise.resolve(saveArtistsResponse)),
                }),
                onChange: jest.fn(),
                value: [
                    { artistInfoId: 1, name: 'abc' },
                    { artistInfoId: 2, name: 'def' },
                ],
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.createArtists(newArtistName).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalled();
                expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
                expect(component.find(ArtistDropdownInfoMessage).exists()).toBeTruthy();
            });
        });
        it('calls createArtists and creates using custom handler', async () => {
            const newArtistName = ['Test1', 'Test2'];
            const data = { validationData: true };
            const testProps = {
                ...vendorProps,
                client,
                onCreate: jest.fn(async () => await Promise.resolve()),
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);
            return await instance.createArtists(newArtistName, data).then(() => {
                expect(client.mutate).toHaveBeenCalledTimes(0);
                expect(testProps.onCreate).toHaveBeenCalledWith(
                    [
                        {
                            artistName: 'Test1',
                            subaccountId: 100,
                            vendorId: 42,
                        },
                        {
                            artistName: 'Test2',
                            subaccountId: 100,
                            vendorId: 42,
                        },
                    ],
                    data
                );
            });
        });
        it('calls handleCreate with compound artist', async () => {
            const newArtistName = 'Test1 & Test2';
            const testProps = {
                client: createClient({
                    ...client,
                    mutate: jest.fn(async () => await Promise.resolve(saveArtistsResponse)),
                }),
                onChange: jest.fn(),
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.handleCreate(newArtistName).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalledTimes(0);
                expect(component).toHaveState('compoundArtist', {
                    artist: newArtistName,
                    artistIsCompound: true,
                    compoundArtist: {
                        artists: ['Test1 ', ' Test2'],
                        isCompound: true,
                        symbol: '&',
                    },
                });
            });
        });
        it('calls handleCreate with compound artist and gets undefined response', async () => {
            const newArtistName = 'Test1';
            const testProps = {
                client: createClient({
                    ...client,
                    mutate: jest.fn(async () => await Promise.resolve(null)),
                }),
                onChange: jest.fn(),
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.handleCreate(newArtistName).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalledTimes(1);
                expect(testProps.onChange).toHaveBeenCalledTimes(0);
            });
        });
        it('calls handleSeparateArtistMessageClose and resets compoundMessageArtist value', () => {
            const component = renderComponent();
            component.setState({
                compoundMessageArtist: { compoundMessageArtist: null },
            });
            getInstance(component).handleSeparateArtistMessageClose();
            expect(component).toHaveState('compoundMessageArtist', null);
        });
        it('calls handleCompoundCancel and has correct values in state', () => {
            const component = renderComponent();
            getInstance(component).handleCompoundCancel();
            expect(component).toHaveState('compoundArtist', null);
        });
        it('calls handleCompoundConfirm and has correct values in state', async () => {
            const newArtistName = 'Test1';
            const component = renderComponent();
            const instance = getInstance(component);
            component.setState({ compoundArtist: { artist: newArtistName } });
            jest.spyOn(instance, 'createArtists').mockImplementation(
                async () => await Promise.resolve()
            );
            jest.spyOn(instance, 'setState');
            return await instance.handleCompoundConfirm().then(() => {
                expect(instance.setState).toHaveBeenCalledWith({
                    isCreateInProgress: true,
                });
                expect(component).toHaveState('isCreateInProgress', false);
                expect(component).toHaveState('compoundArtist', null);
            });
        });
        it('calls handleCreate and fails to create artist', async () => {
            const newArtistName = 'Test';
            const mutationError = new Error('mutation failed');
            const testClient = createClient({
                ...client,
                mutate: jest.fn(async () => await Promise.reject(mutationError)),
            });
            const testProps = {
                client: testClient,
                onChange: jest.fn(),
                onMutationError: jest.fn(),
            };
            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.handleCreate(newArtistName).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalled();
                expect(testProps.onChange).toHaveBeenCalledTimes(0);
                expect(testProps.onMutationError).toHaveBeenCalledWith(mutationError);
            });
        });
        it('calls handleNewArtistDone and calls createArtists handler', async () => {
            const newArtist = 'Test';
            const startTimeNewArtistFlow = new Date();
            const component = renderComponent({ isAnalyticsMode: true });
            const instance = getInstance(component);
            const testData = { data: 'Test' };
            component.setState({ newArtist, startTimeNewArtistFlow });
            jest.spyOn(instance, 'createArtists').mockImplementation(
                async () => await Promise.resolve()
            );
            jest.spyOn(analytics, 'trackSegmentEvent').mockReturnValue();

            return await instance.handleNewArtistDone(testData).then(() => {
                expect(instance.createArtists).toHaveBeenCalledWith([newArtist], testData);
                expect(component.state('newArtist')).toBeNull();
                expect(analytics.trackSegmentEvent).toHaveBeenCalledTimes(1);
            });
        });
        it('calls handleNewArtistClose and resets new artist', () => {
            const newArtist = 'Test';
            const component = renderComponent({ isAnalyticsMode: true });
            const instance = getInstance(component);
            jest.spyOn(analytics, 'trackSegmentEvent').mockReturnValue();
            component.setState({ newArtist });
            instance.handleNewArtistClose();
            expect(component.state('newArtist')).toBeNull();
            expect(analytics.trackSegmentEvent).toHaveBeenCalledTimes(1);
        });
        it('calls handleProfilesByNameFetch and returns correct data', async () => {
            jest.useRealTimers();
            const newArtist = 'Test';
            const testProps = {
                client: createClient({
                    ...client,
                    query: jest.fn(async ({ query }) => {
                        if (getName(query) === getName(ParticipantSpotifySearchGql))
                            return await Promise.resolve(graphqlSpotifyResponse);
                        return await Promise.resolve(graphqlAppleMusicResponse);
                    }),
                }),
            };

            const component = renderComponent(testProps);
            const instance = getInstance(component);

            return await instance.handleProfilesByNameFetch(newArtist).then((data) => {
                const expectedContext = [
                    { ...pagesContext[0], hasMoreResult: false },
                    { ...pagesContext[1], hasMoreResult: false },
                ];
                expect(data).toEqual(expectedContext);
            });
        });
        it('calls handleProfilesByNameFetch and uses custom fetch handler', async () => {
            jest.useRealTimers();
            const newArtist = 'Test';
            const onProfilesFetch = jest.fn(async () => await Promise.resolve([]));
            const component = renderComponent({
                ...vendorProps,
                onProfilesFetch,
            });
            const instance = getInstance(component);
            return await instance.handleProfilesByNameFetch(newArtist).then(() => {
                expect(onProfilesFetch).toHaveBeenCalledWith(newArtist, 42, 100);
            });
        });
        it('calls handleProfilesByNameFetch and returns empty array if query not defined', async () => {
            jest.useRealTimers();
            const instance = getInstance(renderComponent());
            return await instance.handleProfilesByNameFetch('').then((result) => {
                expect(result).toEqual([]);
            });
        });
        it('calls handleProfilesByNameFetch and returns empty tab if error', async () => {
            jest.useRealTimers();
            const newArtist = 'Test';
            const testContext = [
                { ...pagesContext[0], data: [], hasMoreResult: false },
                { ...pagesContext[1], data: [], hasMoreResult: false },
            ];
            const testProps = {
                client: createClient({
                    ...client,
                    query: jest.fn(async () => await Promise.resolve(testContext)),
                }),
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleProfilesByNameFetch(newArtist).then((data) => {
                expect(data).toEqual(testContext);
            });
        });
        it('calls handleProfilesByNameFetch and returns empty tabs if unhandled error', async () => {
            jest.useRealTimers();
            const newArtist = 'Test';
            const testPromise = Promise.resolve();
            testPromise.then = jest.fn(async () => await Promise.reject(new Error('fail')));
            const testProps = {
                client: createClient({
                    ...client,
                    query: jest.fn(async () => await testPromise),
                }),
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleProfilesByNameFetch(newArtist).then((data) => {
                expect(data).toEqual([]);
            });
        });
        it('calls handleProfilesByNameFetch called with localization', async () => {
            const metaLanguageCode = 'ja';
            const newArtist = 'Test';
            const testProps = {
                metaLanguageCode,
                client: createClient({
                    ...client,
                    query: jest.fn(async () => await Promise.resolve()),
                }),
            };

            const instance = getInstance(renderComponent(testProps));
            return await instance.handleProfilesByNameFetch(newArtist).then(() => {
                expect(testProps.client.query).toHaveBeenCalledTimes(2);
                expect(testProps.client.query).toHaveBeenCalledWith({
                    query: ParticipantAppleMusicSearchGql,
                    variables: {
                        query: newArtist,
                        localization: metaLanguageCode,
                    },
                });
                expect(testProps.client.query).toHaveBeenCalledWith({
                    query: ParticipantSpotifySearchGql,
                    variables: {
                        query: newArtist,
                        localization: metaLanguageCode,
                    },
                });
            });
        });
        it('calls handleIncorrectArtistInfoModalOpen and sets correct value in state', () => {
            const component = renderComponent();
            const state = {
                value: 'Test',
                label: 'test',
                data: { name: 'Test' },
            };
            getInstance(component).handleIncorrectArtistInfoModalOpen(state);
            expect(component).toHaveState('incorrectArtistInfo', state);
        });
        it('calls handleIncorrectArtistInfoModalClose and clears value in state', () => {
            const component = renderComponent();
            getInstance(component).handleIncorrectArtistInfoModalClose();
            expect(component).toHaveState('incorrectArtistInfo', null);
        });
        it('calls handleIncorrectArtistInfoModalSubmit and submits with custom handler', () => {
            const testProps = {
                onIncorrectArtistSubmit: jest.fn(),
                ...vendorProps,
            };
            const component = renderComponent(testProps);
            const incorrectArtistInfo = { data: { participantId: 1 } };
            component.setState({ incorrectArtistInfo });
            void getInstance(component).handleIncorrectArtistInfoModalSubmit('Value', 'old', 'new');
            expect(testProps.onIncorrectArtistSubmit).toHaveBeenCalledWith(
                incorrectArtistInfo,
                'Value',
                42
            );
        });
        it('calls handleIncorrectArtistInfoModalSubmit and rejects if null', async () => {
            const testProps = {
                onIncorrectArtistSubmit: undefined,
                ...vendorProps,
            };
            const component = renderComponent(testProps);
            const result = getInstance(component).handleIncorrectArtistInfoModalSubmit(
                'Value',
                'old',
                'new'
            );
            return await expect(result).rejects.toEqual(null);
        });
        it('calls handleIncorrectArtistInfoModalSubmit and rejects if data is null', async () => {
            const testProps = {
                onIncorrectArtistSubmit: undefined,
                ...vendorProps,
            };
            const component = renderComponent(testProps);
            const incorrectArtistInfo = { data: null };
            component.setState({ incorrectArtistInfo });
            const result = getInstance(component).handleIncorrectArtistInfoModalSubmit(
                'Value',
                'old',
                'new'
            );
            return await expect(result).rejects.toBe(incorrectArtistInfo);
        });
        it('calls handleIncorrectArtistInfoModalSubmit and calls request', async () => {
            const testProps = {
                client: createClient({
                    ...client,
                    mutate: jest.fn(
                        async () => await Promise.resolve(graphqlIncorrectArtistResponse)
                    ),
                }),
                ...vendorProps,
                subaccountId: undefined,
            };
            const component = renderComponent(testProps);
            const incorrectArtistInfo = { data: { participantId: 1 } };
            component.setState({ incorrectArtistInfo });
            const result = getInstance(component).handleIncorrectArtistInfoModalSubmit(
                'Value',
                'old',
                'new'
            );
            return await expect(result).resolves.toBe(graphqlIncorrectArtistResponse);
        });
        it('calls handleIncorrectArtistInfoModalSubmit and calls request with error', async () => {
            const error = new Error('fail');
            const testProps = {
                client: createClient({
                    ...client,
                    mutate: jest.fn(async () => await Promise.reject(error)),
                }),
                ...vendorProps,
                subaccountId: undefined,
            };
            const component = renderComponent(testProps);
            const incorrectArtistInfo = { data: { participantId: 1 } };
            component.setState({ incorrectArtistInfo });
            const result = getInstance(component).handleIncorrectArtistInfoModalSubmit(
                'Value',
                'old',
                'new'
            );
            return await expect(result).rejects.toEqual(error);
        });
        it('calls handleIncorrectArtistInfoModalSubmit and submits with default handler', async () => {
            const testProps = {
                onIncorrectArtistSubmit: undefined,
                ...vendorProps,
            };
            const component = renderComponent(testProps);
            const result = getInstance(component).handleIncorrectArtistInfoModalSubmit(
                'Value',
                'old',
                'new'
            );
            return await expect(result).rejects.toBeNull();
        });
        it('calls handleInputChange and keeps NewArtist component if artist not changed', () => {
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleInputChange('Test');
            expect(component).toHaveState('newArtist', 'Test');
        });
        it('calls handleInputChange and closes NewArtist component if artist changed', () => {
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleInputChange('Test_new');
            expect(component).toHaveState('newArtist', null);
        });
        it('calls handleInputChange and keeps Compound prompt if artist not changed', () => {
            const component = renderComponent();
            const compoundArtist = { artist: 'Test1&Test2' };
            component.setState({ compoundArtist });
            getInstance(component).handleInputChange(compoundArtist.artist);
            expect(component).toHaveState('compoundArtist', compoundArtist);
        });
        it('calls handleInputChange and closes Compound prompt if artist changed', () => {
            const component = renderComponent();
            component.setState({ compoundArtist: { artist: 'Test1&Test2' } });
            getInstance(component).handleInputChange('TestA&TestB');
            expect(component).toHaveState('compoundArtist', null);
        });
        it('calls handleSelectProfile with empty selection and skips the execution', () => {
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleSelectProfile(undefined);
            expect(component).toHaveState('newArtist', 'Test');
        });
        it('calls handleSelectProfile with empty profile and does not state change', () => {
            const emptyProfile = { id: '1', profile: null };
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleSelectProfile(emptyProfile);
            expect(component).toHaveState('newArtist', 'Test');
        });
        it('calls handleSelectProfile and updates newArtist if not matches', () => {
            const profile = { id: '1', profile: { name: 'New Test' } };
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleSelectProfile(profile);
            expect(component).toHaveState('newArtist', 'New Test');
        });
        it('calls handleSelectProfile and skips newArtist update if matches', () => {
            const profile = { id: '1', profile: { name: 'Test' } };
            const component = renderComponent();
            component.setState({ newArtist: 'Test' });
            getInstance(component).handleSelectProfile(profile);
            expect(component).toHaveState('newArtist', 'Test');
        });
    });
    describe('with participant_profiles_view feature flag enabled', () => {
        afterEach(() => {
            jest.useFakeTimers();
        });
        it.each([
            ['Null', null as unknown as OnChangeValue],
            ['object where data is null', { data: null } as unknown as OnChangeValue],
        ])('calls handleProfilesFetch with %s and returns empty result', async (_, profile) => {
            jest.useRealTimers();
            const testProps = { isParticipantProfilesViewEnabled: true };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleProfilesFetch(profile).then((data) => {
                expect(data).toEqual([{ store: STORE.SPOTIFY }, { store: STORE.APPLE_MUSIC }]);
            });
        });
        it('calls handleProfilesFetch and returns correct data', async () => {
            jest.useRealTimers();
            jest.spyOn(analytics, 'trackSegmentEvent').mockReturnValue();

            const client = createClient({
                query: jest.fn(async ({ query }) => {
                    if (getName(query) === getName(ParticipantSpotifyArtistGql))
                        return await Promise.resolve(graphqlSpotifyArtistResponse);
                    return await Promise.resolve(graphqlAppleMusicArtistResponse);
                }),
            });
            const component = renderComponent({
                client,
                isParticipantProfilesViewEnabled: true,
                isAnalyticsMode: true,
            });
            const instance = getInstance(component);
            return await instance.handleProfilesFetch(selectedProfile).then((data) => {
                expect(data).toMatchSnapshot();
                expect(analytics.trackSegmentEvent).toHaveBeenCalledTimes(1);
            });
        });
    });
    describe('with "isParticipantProfilesAddEnabled" enabled', () => {
        const testArtist = 'Bon Jovi';
        let component: ShallowWrapper;

        beforeAll(() => {
            component = renderComponent({
                isParticipantProfilesAddEnabled: true,
                isAnalyticsMode: true,
            });
        });

        it('calls handleCreate and sets new artist to state', async () => {
            const instance = getInstance(component);
            jest.spyOn(analytics, 'trackSegmentEvent').mockReturnValue();

            return await instance.handleCreate(testArtist).then(() => {
                expect(component.state('newArtist')).toBe(testArtist);
                expect(analytics.trackSegmentEvent).toHaveBeenCalled();
            });
        });
        it('calls handleCompoundConfirm and sets new artist to state', async () => {
            component.setState({ compoundArtist: { artist: testArtist } });
            return await getInstance(component)
                .handleCompoundConfirm()
                .then(() => {
                    expect(component.state('isCreateInProgress')).toBeFalsy();
                    expect(component.state('newArtist')).toBe(testArtist);
                });
        });
    });
    describe('with "isParticipantProfilesNeo4jSearchEnabled" enabled', () => {
        it('calls handleFetch and fetches unique artists from Neo4j', async () => {
            const testProps = {
                client: createClient({
                    query: jest.fn(async () => await Promise.resolve(neo4jGraphqlResponse)),
                }),
                isParticipantProfilesNeo4jSearchEnabled: true,
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.handleFetch('Test').then((result) => {
                expect(testProps.client.query).toHaveBeenCalled();
                expect(result).toEqual([
                    {
                        participantId: '1',
                        name: 'Artist 1',
                        appleMusicId: 'apple_ID',
                    },
                    {
                        participantId: '2',
                        name: 'Artist 2',
                        spotifyId: 'spotify_ID',
                    },
                ]);
            });
        });
    });
    describe('with labelParticipantCreate + isParticipantDeduplicationEnabled feature flag enabled', () => {
        // eslint-disable-next-line max-len
        it('calls createArtists and successfully creates artist in Neo4j and SQL DB when no artistInfoId from neo4j', async () => {
            const { name: newArtistName } =
                createLabelParticipantResponse.data.createLabelParticipant;
            const mutate = jest
                .fn()
                .mockReturnValueOnce(Promise.resolve(createLabelParticipantResponse))
                .mockReturnValueOnce(Promise.resolve(saveArtistResponse));
            const testProps = {
                ...vendorProps,
                client: createClient({ mutate }),
                isParticipantProfilesNeo4jCreateEnabled: true,
                isParticipantDeduplicationEnabled: true,
                onChange: jest.fn(),
                value: [{ artistInfoId: 1, name: 'Test 0' }],
            };
            const createData = {
                tabSpotify: {
                    id: '1',
                    store: STORE.SPOTIFY,
                    selectedProfile: pagesContext[0].data[0],
                },
                tabAppleMusic: {
                    id: '2',
                    store: STORE.APPLE_MUSIC,
                    selectedProfile: pagesContext[1].data[1],
                },
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName], createData).then(() => {
                expect(mutate).toHaveBeenCalledTimes(2);
                expect(mutate.mock.calls[0][0].variables).toEqual({
                    appleMusicId: '111285985',
                    name: newArtistName,
                    spotifyId: '6h2bWHWTJL38N8dqocVaif',
                    subaccountId: 100,
                    vendorId: 42,
                });
                expect(mutate.mock.calls[1][0].variables).toEqual({
                    create: [
                        {
                            artistName: 'Test',
                            subaccountId: 100,
                            vendorId: 42,
                        },
                    ],
                });
            });
        });
        // eslint-disable-next-line max-len
        it('calls createArtists and successfully creates artist in Neo4j and not SQL DB when artistInfoId is provided from neo4j', async () => {
            const { name: newArtistName } =
                createLabelParticipantResponse.data.createLabelParticipant;
            const { artistId, artistName } = saveArtistResponse.data.saveArtists[0];
            const onChangeData = [
                { artistInfoId: 1, name: 'Test 0' },
                {
                    appleMusicId: 'apple_ID',
                    artistInfoId: artistId,
                    name: artistName,
                    participantId: '503',
                    spotifyId: 'spotify_ID',
                },
            ];
            const mutate = jest
                .fn()
                .mockReturnValueOnce(Promise.resolve(createLabelParticipantWithArtistInfoResponse));

            const testProps = {
                ...vendorProps,
                client: createClient({ mutate }),
                isParticipantProfilesNeo4jCreateEnabled: true,
                isParticipantDeduplicationEnabled: true,
                onChange: jest.fn(),
                value: [{ artistInfoId: 1, name: 'Test 0' }],
            };
            const createData = {
                tabSpotify: {
                    id: '1',
                    store: STORE.SPOTIFY,
                    selectedProfile: pagesContext[0].data[0],
                },
                tabAppleMusic: {
                    id: '2',
                    store: STORE.APPLE_MUSIC,
                    selectedProfile: pagesContext[1].data[1],
                },
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName], createData).then(() => {
                expect(mutate).toHaveBeenCalledTimes(1);
                expect(mutate.mock.calls[0][0].variables).toEqual({
                    appleMusicId: '111285985',
                    name: newArtistName,
                    spotifyId: '6h2bWHWTJL38N8dqocVaif',
                    subaccountId: 100,
                    vendorId: 42,
                });
                expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
            });
        });
        it('calls createArtists and failed to create an artist in Neo4j', async () => {
            const newArtistName = 'Test 1';
            const mutationError = new Error('mutation failed');
            const client = createClient({
                mutate: jest.fn(async () => await Promise.reject(mutationError)),
            });
            const testProps = {
                client,
                isParticipantProfilesNeo4jCreateEnabled: true,
                isParticipantDeduplicationEnabled: true,
                onChange: jest.fn(),
                onMutationError: jest.fn(),
                value: [{ artistInfoId: 1, name: 'Test 0' }],
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName]).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalledTimes(1);
                expect(testProps.onChange).toHaveBeenCalledTimes(0);
                expect(testProps.onMutationError).toHaveBeenNthCalledWith(1, mutationError);
            });
        });
    });
    describe('with labelParticipantCreate feature flag enabled and isParticipantDeduplicationEnabled disabled', () => {
        it('calls createArtists and successfully creates artist in Neo4j and SQL DB', async () => {
            const { name: newArtistName } =
                createLabelParticipantResponse.data.createLabelParticipant;
            const { artistId, artistName } = saveArtistResponse.data.saveArtists[0];
            const onChangeData = [
                { artistInfoId: 1, name: 'Test 0' },
                {
                    appleMusicId: 'apple_ID',
                    artistInfoId: artistId,
                    name: artistName,
                    participantId: '503',
                    spotifyId: 'spotify_ID',
                },
            ];
            const mutate = jest
                .fn()
                .mockReturnValueOnce(Promise.resolve(saveArtistResponse))
                .mockReturnValueOnce(Promise.resolve(createLabelParticipantResponse));
            const testProps = {
                ...vendorProps,
                client: createClient({ mutate }),
                isParticipantProfilesNeo4jCreateEnabled: true,
                onChange: jest.fn(),
                value: [{ artistInfoId: 1, name: 'Test 0' }],
            };
            const createData = {
                tabSpotify: {
                    id: '1',
                    store: STORE.SPOTIFY,
                    selectedProfile: pagesContext[0].data[0],
                },
                tabAppleMusic: {
                    id: '2',
                    store: STORE.APPLE_MUSIC,
                    selectedProfile: pagesContext[1].data[1],
                },
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName], createData).then(() => {
                expect(mutate).toHaveBeenCalledTimes(2);
                expect(mutate.mock.calls[1][0].variables).toEqual({
                    appleMusicId: '111285985',
                    name: newArtistName,
                    spotifyId: '6h2bWHWTJL38N8dqocVaif',
                    subaccountId: 100,
                    vendorId: 42,
                });
                expect(mutate.mock.calls[0][0].variables).toEqual({
                    create: [
                        {
                            artistName: 'Test',
                            subaccountId: 100,
                            vendorId: 42,
                        },
                    ],
                });
                expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
            });
        });
        it(`calls createArtists and successfully creates artist in Neo4j
           and isStripOutHiddenSpacesInWorkstationEnabled feature flag enabled`, async () => {
            const newArtistName = ' Test '; // String with zero-width non-breaking spaces
            const expectedArtistName = 'Test';
            const { artistId, artistName } = saveArtistResponse.data.saveArtists[0];
            const onChangeData = [
                { artistInfoId: 1, name: newArtistName },
                {
                    appleMusicId: 'apple_ID',
                    artistInfoId: artistId,
                    name: artistName,
                    participantId: '503',
                    spotifyId: 'spotify_ID',
                },
            ];
            const mutate = jest
                .fn()
                .mockReturnValueOnce(Promise.resolve(saveArtistResponse))
                .mockReturnValueOnce(Promise.resolve(createLabelParticipantResponse));
            const testProps = {
                ...vendorProps,
                client: createClient({ mutate }),
                isParticipantProfilesNeo4jCreateEnabled: true,
                isStripOutHiddenSpacesInWorkstationEnabled: true,
                onChange: jest.fn(),
                value: [{ artistInfoId: 1, name: newArtistName }],
            };
            const createData = {
                tabSpotify: {
                    id: '1',
                    store: STORE.SPOTIFY,
                    selectedProfile: pagesContext[0].data[0],
                },
                tabAppleMusic: {
                    id: '2',
                    store: STORE.APPLE_MUSIC,
                    selectedProfile: pagesContext[1].data[1],
                },
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName], createData).then(() => {
                expect(mutate).toHaveBeenCalledTimes(2);
                expect(mutate.mock.calls[1][0].variables).toEqual({
                    appleMusicId: '111285985',
                    name: expectedArtistName,
                    spotifyId: '6h2bWHWTJL38N8dqocVaif',
                    subaccountId: 100,
                    vendorId: 42,
                });
                expect(mutate.mock.calls[0][0].variables).toEqual({
                    create: [
                        {
                            artistName: expectedArtistName,
                            subaccountId: 100,
                            vendorId: 42,
                        },
                    ],
                });
                expect(testProps.onChange).toHaveBeenCalledWith(onChangeData);
            });
        });
        it('calls createArtists and failed to create an artist in Neo4j', async () => {
            const newArtistName = 'Test 1';
            const mutationError = new Error('mutation failed');
            const testProps = {
                client: createClient({
                    mutate: jest.fn(async () => await Promise.reject(mutationError)),
                }),
                isParticipantProfilesNeo4jCreateEnabled: true,
                onChange: jest.fn(),
                onMutationError: jest.fn(),
                value: [{ artistInfoId: 1, name: 'Test 0' }],
            };
            const instance = getInstance(renderComponent(testProps));
            return await instance.createArtists([newArtistName]).then(() => {
                expect(testProps.client.mutate).toHaveBeenCalledTimes(2);
                expect(testProps.onChange).toHaveBeenCalledTimes(0);
                expect(testProps.onMutationError).toHaveBeenNthCalledWith(1, mutationError);
                expect(testProps.onMutationError).toHaveBeenNthCalledWith(2, mutationError);
            });
        });
    });
    describe('when component did mount and productId is provided', () => {
        it('calls labelParticipantByProduct query', () => {
            const client = createClient({
                query: jest.fn(async () => await Promise.resolve()),
            });
            const testProps = { productId: '123', client };
            renderComponent(testProps);
            expect(client.query).toHaveBeenCalledWith({
                query: LabelParticipantsByProductQueryGql,
                variables: { productId: '123' },
                fetchPolicy: 'network-only',
            });
        });
    });
    describe('when component did mount and projectId is provided', () => {
        it('calls labelParticipantByProject query', () => {
            const client = createClient({
                query: jest.fn(async () => await Promise.resolve()),
            });
            const testProps = {
                projectId: '123',
                vendorId: 123,
                subaccountId: 456,
                client,
            };
            renderComponent(testProps);
            expect(client.query).toHaveBeenCalledWith({
                query: LabelParticipantByProjectGql,
                variables: {
                    projectId: '123',
                    vendorId: 123,
                    subaccountId: 456,
                },
                fetchPolicy: 'network-only',
            });
        });
    });
});
