import React from 'react';
import {
    ApolloError,
    ApolloQueryResult,
    OperationVariables,
    QueryTuple,
} from '@apollo/client';
import {
    act,
    findByText,
    fireEvent,
    queryByText,
    screen,
    waitFor,
} from '@testing-library/react';
import { Segment } from '@theorchard/suite-frontend';
import { renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import {
    fireSelectForInput,
    userEvent,
    wait,
    waitForLoaded,
} from 'lib/test/helpers';
import { FEATURE_FLAGS } from 'src/constants';
import * as createComposition from 'src/data/mutations/createComposition/createComposition';
import * as createGsrFromSpotify from 'src/data/mutations/createGlobalSoundRecordingFromSpotify/createGlobalSoundRecordingFromSpotify';
import * as updateComposition from 'src/data/mutations/updateComposition/updateComposition';
import { PublishingCompositionsQuery } from 'src/data/queries/compositions/__generated__/compositions';
import * as compositions from 'src/data/queries/compositions/compositions';
import * as isrcEntities from 'src/data/queries/isrcEntities/isrcEntities';
import * as orchardLabelById from 'src/data/queries/orchardLabel/orchardLabelById';
import * as orchardLabels from 'src/data/queries/orchardLabels/orchardLabels';
import { PublishingPublishersQuery } from 'src/data/queries/publishers/__generated__/publishers';
import * as publishers from 'src/data/queries/publishers/publishers';
import { PublishingSongWritersQuery } from 'src/data/queries/songWritersSearch/__generated__/songWritersSearch';
import * as songWritersSearch from 'src/data/queries/songWritersSearch/songWritersSearch';
import * as spotifyTrackSearch from 'src/data/queries/spotifyTrackSearch/spotifyTrackSearch';
import * as trackSearch from 'src/data/queries/trackSearch/trackSearch';
import * as applicationContext from 'src/utils/applicationContext';
import * as featuresMock from 'src/utils/features';
import { songUrl } from 'src/utils/urls';
import * as helpersMock from '../helpers';
import {
    compositionMock,
    controlledSongWritersWithoutRequired,
    createdGsr,
    orchardLabelsMock,
    publishersMock,
    songWritersMock,
    songWritersMockNoControlled,
    spotifyTrackSearchResult,
    trackSearchResult,
} from '../mocks';
import NewSong from '../newSong';

const pushMock = jest.fn((route: string) => createMemoryHistory().push(route));

let locationPathnameMock = '/';

jest.mock('react-router-dom', () => {
    const originalModule = jest.requireActual('react-router-dom');

    return {
        ...originalModule,
        useHistory: jest.fn(() => ({
            ...originalModule.useHistory(),
            push: pushMock,
            location: { pathname: locationPathnameMock },
        })),
    };
});

describe('<NewSong>', () => {
    const searchPlaceholder = 'Search by track name';

    const defaultProps = {
        associatedSoundRecording: null,
        composition: null,
        debounceTime: 0,
    };

    let createMock = jest.fn();
    let updateMock = jest.fn();

    const associatedSoundRecording = {
        id: '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
        isrc: 'QMFMF1565547',
        name: 'Song',
        performers: ['Sarry'],
    };

    const associatedSoundRecordingWithLsr = {
        ...associatedSoundRecording,
        isLsr: true,
    };

    const renderComponent = (customProps = {}, contextProps = {}) => {
        const props = {
            ...defaultProps,
            ...customProps,
        };

        return renderInAppContext(<NewSong {...props} />, contextProps);
    };

    const mockAssociatedSoundRecordings = () => {
        jest.spyOn(trackSearch, 'useLazyTrackSearch').mockReturnValue([
            jest.fn(),
            trackSearchResult,
        ]);
        jest.spyOn(
            spotifyTrackSearch,
            'useLazySpotifyTrackSearch'
        ).mockReturnValue([jest.fn(), spotifyTrackSearchResult]);
        jest.spyOn(
            createGsrFromSpotify,
            'useCreateGlobalSoundRecordingFromSpotify'
        ).mockImplementation(
            (fn: Function) => () =>
                fn({
                    createGlobalSoundRecordingFromSpotify: {
                        ...createdGsr,
                    },
                })
        );

        jest.spyOn(Segment, 'trackEvent');

        jest.spyOn(
            songWritersSearch,
            'usePublishingSongWriters'
        ).mockReturnValue({
            error: undefined,
            loading: false,
            data: songWritersMock,
            refetch: async () =>
                await Promise.resolve(
                    {} as ApolloQueryResult<PublishingSongWritersQuery>
                ),
            networkStatus: 7,
        });
        jest.spyOn(publishers, 'usePublishingPublishers').mockReturnValue({
            error: undefined,
            loading: false,
            data: publishersMock,
            refetch: async () =>
                await Promise.resolve(
                    {} as ApolloQueryResult<PublishingPublishersQuery>
                ),
            networkStatus: 7,
        });
        jest.spyOn(orchardLabels, 'useOrchardLabels').mockReturnValue({
            loading: false,
            error: undefined,
            data: orchardLabelsMock,
        });
        jest.spyOn(
            orchardLabelById,
            'useOrchardLabelByIdQuery'
        ).mockReturnValue({
            loading: false,
            error: undefined,
            data: orchardLabelsMock[0],
        });
    };

    beforeEach(() => {
        mockAssociatedSoundRecordings();

        createMock = jest.fn();
        updateMock = jest.fn();

        jest.spyOn(createComposition, 'useCreateComposition').mockReturnValue(
            createMock
        );
        jest.spyOn(updateComposition, 'useUpdateComposition').mockReturnValue(
            updateMock
        );
        jest.spyOn(applicationContext, 'useApplicationContext').mockReturnValue(
            {
                label: orchardLabelsMock[0],
                setLabel: jest.fn(),
                labelAlt: null,
                setLabelAlt: jest.fn(),
                pageFilters: { songPageSongWriters: null, brand: null },
                updatePageFilters: jest.fn(),
            }
        );
        jest.spyOn(isrcEntities, 'useIsrcEntitiesFetcher').mockReturnValue(
            jest.fn().mockResolvedValue(null)
        );
        const mockDuplicateCompositionId = 'test1';
        jest.spyOn(
            compositions,
            'useLazyCheckSameTitleCompositionExistsQuery'
        ).mockImplementation(
            cb =>
                [
                    () => {
                        act(() => {
                            cb(mockDuplicateCompositionId);
                        });
                    },
                    {},
                ] as QueryTuple<PublishingCompositionsQuery, OperationVariables>
        );
    });

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

    test('can show errors', async () => {
        renderComponent();
        await waitForLoaded();

        await userEvent.click(screen.getByText('Add Alternate Song Title'));
        await userEvent.type(screen.getByLabelText('ISWC'), 'showr');
        await userEvent.click(screen.getByText('Save Draft'));

        expect(screen.getAllByText('This field is required').length).toBe(3);
        expect(
            screen.getByText('This is not the correct format for an ISWC')
        ).toBeTruthy();
    });

    test('can load from template', async () => {
        renderComponent({ associatedSoundRecording });
        await waitForLoaded();

        expect(screen.getByLabelText('Song Title')).toHaveValue('Song');
    });

    test('can submit with default test data', async () => {
        const { container } = renderComponent({
            composition: compositionMock,
            associatedSoundRecording,
        });
        await waitForLoaded();
        const mainTitle = container.querySelector('#mainTitle');
        if (!mainTitle) throw new Error('Could not find #mainTitle');
        fireEvent.change(mainTitle, { target: { value: 'cool title' } });

        //check Lyrics as contribution for songwriter one
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[1]
        );

        //check Music as contribution for songwriter two
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[3]
        );

        //check Lyrics as contribution for songwriter two
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[4]
        );

        //check Arranger as contribution for songwriter two, so other two contributions are disabled
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[5]
        );

        await userEvent.click(screen.getByTestId('update-song-button'));

        expect(updateMock).toHaveBeenCalledWith({
            variables: {
                title: 'cool title',
                draft: false,
                id: 'd837d5a6-05f6-4ea1-bcd5-e85fc249ef2c',
                alternateTitles: [],
                iswc: null,
                associatedGlobalSoundRecordings: [
                    '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                ],
                associatedLabelSoundRecordings: [],
                containsPublicDomain: false,
                containsSample: false,
                agreements: [
                    {
                        agreementId: 'a1',
                        containsPublicDomain: false,
                        containsSample: false,
                        split: '50',
                        hasMusicContribution: true,
                        hasLyricsContribution: true,
                        hasArrangerContribution: false,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                    {
                        agreementId: 'a2',
                        split: '50',
                        containsPublicDomain: false,
                        containsSample: false,
                        hasMusicContribution: false,
                        hasLyricsContribution: false,
                        hasArrangerContribution: true,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                ],
                label: {
                    uuid: orchardLabelsMock[0].uuid,
                },
            },
        });
    });

    test('can not submit with default test data if songwriters are not controlled', async () => {
        jest.spyOn(
            songWritersSearch,
            'usePublishingSongWriters'
        ).mockReturnValue({
            loading: false,
            error: undefined,
            data: songWritersMockNoControlled,
            refetch: async () =>
                await Promise.resolve(
                    {} as ApolloQueryResult<PublishingSongWritersQuery>
                ),
            networkStatus: 7,
        });
        const { container } = renderComponent({ associatedSoundRecording });
        await act(async () => {
            await waitForLoaded();
        });

        await userEvent.clear(screen.getByLabelText('Song Title'));
        await userEvent.type(screen.getByLabelText('Song Title'), 'cool title');

        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[0],
            'Mike Lorenz'
        );

        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[0],
            '100'
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[0]
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[1]
        );

        await userEvent.click(screen.getByText('Submit'));

        await waitFor(() => {
            expect(
                screen.getByText(
                    'There must be at least one controlled songwriter'
                )
            ).toBeTruthy();
        });
        expect(Segment.trackEvent).toHaveBeenCalledTimes(0);
    });

    test('can submit with default test data if songwriters are not controlled AND ALLOW_UNCONTROLLED_WRITERS is ON', async () => {
        jest.spyOn(featuresMock, 'useUncontrolledWritersFF').mockReturnValue(
            true
        );

        const { container } = renderComponent({
            composition: {
                ...compositionMock,
                agreements: compositionMock.agreements.map(a => ({
                    ...a,
                    agreement: {
                        ...a.agreement,
                        controlled: false,
                    },
                })),
            },
            associatedSoundRecording,
        });
        await waitForLoaded();

        const mainTitle = container.querySelector('#mainTitle');
        if (!mainTitle) throw new Error('Could not find #mainTitle');
        fireEvent.change(mainTitle, { target: { value: 'cool title' } });

        //check Lyrics as contribution on songwriter one
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[1]
        );

        //uncheck Music as contribution on songwriter two
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[3]
        );

        //check Lyrics as contribution on songwriter two
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[4]
        );

        await userEvent.click(screen.getByText('Update'));

        expect(updateMock).toHaveBeenCalledWith({
            variables: {
                title: 'cool title',
                draft: false,
                id: 'd837d5a6-05f6-4ea1-bcd5-e85fc249ef2c',
                alternateTitles: [],
                iswc: null,
                associatedGlobalSoundRecordings: [
                    '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                ],
                associatedLabelSoundRecordings: [],
                containsPublicDomain: false,
                containsSample: false,
                agreements: [
                    {
                        agreementId: 'a1',
                        containsPublicDomain: false,
                        containsSample: false,
                        split: '50',
                        hasMusicContribution: true,
                        hasLyricsContribution: true,
                        hasArrangerContribution: false,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                    {
                        agreementId: 'a2',
                        split: '50',
                        containsPublicDomain: false,
                        containsSample: false,
                        hasMusicContribution: false,
                        hasLyricsContribution: true,
                        hasArrangerContribution: false,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                ],
                label: {
                    uuid: orchardLabelsMock[0].uuid,
                },
            },
        });

        expect(Segment.trackEvent).toHaveBeenCalledTimes(1);
    });

    test('can submit with default test data if songwriters are controlled WITHOUT .ipi .pro props', async () => {
        jest.spyOn(
            songWritersSearch,
            'usePublishingSongWriters'
        ).mockReturnValue({
            loading: false,
            error: undefined,
            data: controlledSongWritersWithoutRequired,
            refetch: async () =>
                await Promise.resolve(
                    {} as ApolloQueryResult<PublishingSongWritersQuery>
                ),
            networkStatus: 7,
        });

        const { container } = renderComponent({ associatedSoundRecording });
        await act(async () => {
            await waitForLoaded();
        });
        await userEvent.clear(screen.getByLabelText('Song Title'));
        await userEvent.type(screen.getByLabelText('Song Title'), 'cool title');

        await userEvent.click(screen.getByText('Add Co-Writer'));
        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[0],
            'Mike Lorenz'
        );

        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[0],
            '100'
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[0]
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[1]
        );

        await userEvent.click(screen.getByText('Submit'));

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                title: 'cool title',
                draft: false,
                alternateTitles: [],
                iswc: null,
                associatedGlobalSoundRecordings: [
                    '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                ],
                associatedLabelSoundRecordings: [],
                containsPublicDomain: false,
                containsSample: false,
                agreements: [
                    {
                        agreementId: 'a1',
                        containsPublicDomain: false,
                        containsSample: false,
                        split: '100',
                        hasMusicContribution: true,
                        hasLyricsContribution: true,
                        hasArrangerContribution: false,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                ],
                label: {
                    uuid: orchardLabelsMock[0].uuid,
                },
            },
        });

        expect(Segment.trackEvent).toHaveBeenCalledTimes(1);
    });

    test('can update draft', async () => {
        const composition = {
            id: '1',
            title: '',
            alternateTitles: [],
            agreements: [],
            vendor: {
                id: {
                    subaccountId: 0,
                    vendorId: 2021,
                },
                uuid: 'qwert-1234',
            },
        };

        renderComponent({ composition });

        await userEvent.type(screen.getByLabelText('Song Title'), 'cool title');
        await userEvent.click(screen.getByText('Save Draft'));

        await waitFor(() => {
            expect(updateMock).toHaveBeenCalledWith({
                variables: {
                    id: '1',
                    title: 'cool title',
                    draft: true,
                    alternateTitles: [],
                    iswc: null,
                    associatedGlobalSoundRecordings: [],
                    associatedLabelSoundRecordings: [],
                    agreements: [],
                    label: {
                        uuid: orchardLabelsMock[0].uuid,
                    },
                    containsPublicDomain: false,
                    containsSample: false,
                },
            });
        });
    });

    test('can load from passed in composition', async () => {
        const composition = {
            id: '1',
            alternateTitles: [],
            title: 'Power',
            agreements: [],
        };

        renderComponent({ composition });
        await waitForLoaded();

        expect(screen.getByLabelText('Song Title')).toHaveValue('Power');
    });

    test('adds ownership in songwriters', async () => {
        const { container } = renderComponent();
        await waitForLoaded();

        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[0],
            'Mike Lorenz'
        );
        await userEvent.click(screen.getByText('Add Co-Writer'));
        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[1],
            'Jorja Smith'
        );
        await userEvent.click(screen.getByText('Add Co-Writer'));
        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[2],
            'Tom Jones'
        );

        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[0],
            '20'
        );
        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[1],
            '5'
        );
        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[2],
            '10'
        );

        expect(screen.getByText('35%')).toBeTruthy();
    });

    test('can remove an Associated Sound Recording', async () => {
        const { container } = renderComponent({ associatedSoundRecording });
        await waitForLoaded();

        expect(screen.getByText('Sarry')).toBeTruthy();
        const removeSoundRecording = container.querySelector(
            '.NewSong-sound-recording-remove'
        );
        if (!removeSoundRecording)
            throw new Error('Could not find .NewSong-sound-recording-remove');
        fireEvent.click(removeSoundRecording);

        expect(screen.queryByText('Sarry')).toBeFalsy();
    });

    test('can add new songwriters', async () => {
        const { container } = renderComponent();
        await waitForLoaded();

        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[0],
            'Add New Songwriter'
        );

        expect(screen.getByText('Register a New Songwriter')).toBeTruthy();
    });

    test('can search for an Associated Sound Recording', async () => {
        const { container } = renderComponent(1);
        await waitForLoaded();

        expect(
            container.querySelectorAll('.NewSong-sound-recording')
        ).toHaveLength(0);

        expect(screen.getByText('Search for a sound recording')).toBeTruthy();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        // picks smallest spotify image
        expect(
            container.querySelectorAll(
                '.NewSong-sound-recordings-search-image'
            )[1]
        ).toHaveProperty('src', 'http://localhost/goodurl');
        // filters out duplicate spotify
        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(3);

        await userEvent.click(screen.getByText('Dont Leave Me Alone'));
        expect(
            container.querySelectorAll('.NewSong-sound-recording')
        ).toHaveLength(1);

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        // filters out already selected result
        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(2);

        expect(Segment.trackEvent).toHaveBeenCalledWith(
            'Click - Added Associated Sound Recording',
            { category: 'New Song' }
        );
    });

    test('shows error when searching for an Associated Sound Recording fails because of Spotify', async () => {
        jest.spyOn(
            spotifyTrackSearch,
            'useLazySpotifyTrackSearch'
        ).mockReturnValue([
            jest.fn(),
            {
                loading: false,
                data: [],
                error: new ApolloError({ errorMessage: 'Error' }),
                called: true,
            },
        ]);

        renderComponent();
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        expect(
            screen.getByText('We were unable to fetch results from Spotify')
        ).toBeTruthy();
    });

    test('shows error when searching for an Associated Sound Recording fails (no Spotify)', async () => {
        jest.spyOn(trackSearch, 'useLazyTrackSearch').mockReturnValue([
            jest.fn(),
            {
                loading: false,
                songs: [],
                error: new ApolloError({ errorMessage: 'Error' }),
                called: true,
            },
        ]);

        renderComponent();
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        expect(
            screen.getByText('We are unable to fetch all search results')
        ).toBeTruthy();
    });

    test('shows error when searching for an Associated Sound Recording fails because Spotify and another one', async () => {
        jest.spyOn(
            spotifyTrackSearch,
            'useLazySpotifyTrackSearch'
        ).mockReturnValue([
            jest.fn(),
            {
                loading: false,
                data: [],
                error: new ApolloError({ errorMessage: 'Error' }),
                called: true,
            },
        ]);
        jest.spyOn(trackSearch, 'useLazyTrackSearch').mockReturnValue([
            jest.fn(),
            {
                loading: false,
                songs: [],
                error: new ApolloError({ errorMessage: 'Error' }),
                called: true,
            },
        ]);

        renderComponent();
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        expect(
            screen.getByText('We are unable to fetch all search results')
        ).toBeTruthy();
    });

    test('shows `There are no results` message for an Associated Sound Recording', async () => {
        jest.spyOn(trackSearch, 'useLazyTrackSearch').mockReturnValue([
            jest.fn(),
            {
                loading: false,
                called: true,
                songs: [],
                error: undefined,
            },
        ]);
        jest.spyOn(
            spotifyTrackSearch,
            'useLazySpotifyTrackSearch'
        ).mockReturnValue([
            jest.fn(),
            { loading: false, data: [], error: undefined, called: false },
        ]);

        const { container } = renderComponent();
        await waitForLoaded();

        expect(screen.getByText('Search for a sound recording')).toBeTruthy();

        await userEvent.type(
            screen.getByPlaceholderText('Search by track name'),
            'honey'
        );
        await waitForLoaded();

        expect(screen.getByText('There are no results')).toBeTruthy();
        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(0);
    });

    test('can create GSR from Spotify search result', async () => {
        const { container } = renderComponent();
        await waitForLoaded();

        expect(
            container.querySelectorAll('.NewSong-sound-recording')
        ).toHaveLength(0);

        expect(screen.getByText('Search for a sound recording')).toBeTruthy();
        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        // picks smallest spotify image
        expect(
            container.querySelectorAll(
                '.NewSong-sound-recordings-search-image'
            )[1]
        ).toHaveProperty('src', 'http://localhost/goodurl');
        // filters out duplicate spotify
        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(3);

        await userEvent.click(screen.getByText('Billy Spot'));

        expect(
            container.querySelectorAll('.NewSong-sound-recording')
        ).toHaveLength(1);

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'honey'
        );
        await waitForLoaded();

        // filters out already selected result
        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(2);

        expect(Segment.trackEvent).toHaveBeenCalledWith(
            'Click - Added Associated Sound Recording',
            { category: 'New Song' }
        );
    });

    test('will show errors alert on submit', async () => {
        renderComponent();
        await waitForLoaded();

        await userEvent.click(screen.getByText('Submit'));

        expect(screen.getByText('Song title is required')).toBeTruthy();
        expect(
            screen.getByText('There must be at least one controlled songwriter')
        ).toBeTruthy();
        expect(
            screen.getAllByText('Ownership must add up to 100%')
        ).toBeTruthy();
        expect(
            screen.getAllByText(
                'Music Contribution must be selected for at least one songwriter'
            )
        ).toBeTruthy();
        expect(
            screen.getByText(
                'A writer has not contributed music, lyrics or arranger to the song'
            )
        ).toBeTruthy();
    });

    test('will navigate from a draft song to the draft songs tab', async () => {
        renderComponent();
        await waitForLoaded();
        await userEvent.click(screen.getByText('Cancel'));
        expect(pushMock).toHaveBeenCalledWith('/songs/drafts?page=1');
    });

    test('will navigate from a submitted song to the submitted songs tab', async () => {
        renderComponent({
            composition: compositionMock,
            associatedSoundRecording,
        });
        await waitForLoaded();
        await userEvent.click(screen.getByText('Cancel'));
        expect(pushMock).toHaveBeenCalledWith('/songs/submitted?page=1');
    });

    test('will not let you add the same person twice', async () => {
        const { container } = renderComponent();
        await waitForLoaded();
        await fireSelectForInput(
            container.querySelectorAll('.NewSong-songwriters-name input')[0],
            'Mike Lorenz'
        );

        await userEvent.click(screen.getByText('Add Co-Writer'));
        await userEvent.click(
            container.querySelectorAll('.NewSong-songwriters-name input')[1]
        );

        const select = container.querySelectorAll(
            '.NewSong-songwriters-name input'
        )[1];
        const closestSelect = select
            .closest('.Select')
            ?.querySelector('.Select__menu');
        if (!closestSelect) throw new Error('Could not find .Select__menu');
        await findByText(closestSelect as HTMLElement, 'Jorja Smith', {
            exact: false,
        });
        expect(
            queryByText(closestSelect as HTMLElement, 'Mike Lorenz', {
                exact: false,
            })
        ).toBeFalsy();
    });

    test('label dropdown is not disabled for a new song', async () => {
        const { container } = renderComponent({ associatedSoundRecording });
        await waitForLoaded();

        expect(
            container.querySelector('.LabelSearchDropdown .Select--is-disabled')
        ).toBeFalsy();
    });

    test('disables Associated Label dropdown for a submitted song', async () => {
        locationPathnameMock = songUrl(compositionMock.id);

        const { container } = renderComponent({ composition: compositionMock });
        await waitForLoaded();

        expect(
            container.querySelector('.LabelSearchDropdown .Select--is-disabled')
        ).toBeTruthy();
    });

    test('allows adding an associated sound recording to a submitted song', async () => {
        const { container } = renderComponent({ composition: compositionMock });
        await waitForLoaded();

        expect(
            container.querySelectorAll('.NewSong-sound-recording')
        ).toHaveLength(1);

        expect(screen.getByText('Search for a sound recording')).toBeTruthy();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'leave'
        );
        await waitForLoaded();

        expect(
            container.querySelectorAll('.NewSong-sound-recordings-search-row')
        ).toHaveLength(3);

        await userEvent.click(screen.getByText('Dont Leave Me Alone'));
        await userEvent.click(screen.getByText('Update'));

        expect(updateMock).toHaveBeenCalledWith({
            variables: {
                associatedGlobalSoundRecordings: [
                    '145',
                    'ju2eb556-d1f9-45c3-mme3-d65f67cc736c',
                ],
                associatedLabelSoundRecordings: [],
                containsPublicDomain: false,
                containsSample: false,
                alternateTitles: [],
                draft: false,
                id: 'd837d5a6-05f6-4ea1-bcd5-e85fc249ef2c',
                iswc: null,
                label: {
                    uuid: orchardLabelsMock[0].uuid,
                },
                agreements: [
                    {
                        agreementId: 'a1',
                        containsPublicDomain: false,
                        containsSample: false,
                        hasLyricsContribution: false,
                        hasArrangerContribution: false,
                        hasMusicContribution: true,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                        split: '50',
                    },
                    {
                        agreementId: 'a2',
                        containsPublicDomain: false,
                        containsSample: false,
                        hasLyricsContribution: false,
                        hasArrangerContribution: false,
                        hasMusicContribution: true,
                        split: '50',
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                    },
                ],
                title: 'Power',
            },
        });
    });

    test('can submit when only one label in account', async () => {
        const orchardLabelsMockOneLabel = [
            {
                name: 'Cow',
                id: {
                    subaccountId: 2,
                    vendorId: 1,
                },
                uuid: orchardLabelsMock[0].uuid,
                __typename: 'Subaccount' as const,
            },
        ];

        jest.spyOn(orchardLabels, 'useOrchardLabels').mockReturnValue({
            loading: false,
            error: undefined,
            data: orchardLabelsMockOneLabel,
        });

        jest.spyOn(applicationContext, 'useApplicationContext').mockReturnValue(
            {
                label: orchardLabelsMockOneLabel[0],
                setLabel: jest.fn(),
                labelAlt: null,
                setLabelAlt: jest.fn(),
                pageFilters: { songPageSongWriters: null, brand: null },
                updatePageFilters: jest.fn(),
            }
        );

        const { container } = renderComponent({ associatedSoundRecording });
        await waitForLoaded();

        await userEvent.clear(screen.getByLabelText('Song Title'));
        await userEvent.type(screen.getByLabelText('Song Title'), 'cool title');

        await userEvent.click(
            container.querySelectorAll('.NewSong-songwriters-name input')[0]
        );
        await userEvent.click(screen.getByText('Mike Lorenz'));
        await userEvent.click(screen.getByText('Add Co-Writer'));
        await userEvent.click(
            container.querySelectorAll('.NewSong-songwriters-name input')[1]
        );
        await userEvent.click(screen.getByText('Jorja Smith'));

        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[0],
            '69.89'
        );
        await userEvent.type(
            container.querySelectorAll(
                '.NewSong-songwriters-ownership input'
            )[1],
            '30.11'
        );

        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[0]
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[2]
        );
        await userEvent.click(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[3]
        );

        await userEvent.click(screen.getByText('Submit'));

        await waitFor(() => {
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                'Click - Create Song',
                {
                    category: 'New Song',
                    alternateTitles: [],
                    iswc: '',
                    title: 'cool title',
                }
            );
        });

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                title: 'cool title',
                draft: false,
                alternateTitles: [],
                iswc: null,
                associatedGlobalSoundRecordings: [
                    '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                ],
                associatedLabelSoundRecordings: [],
                containsPublicDomain: false,
                containsSample: false,
                agreements: [
                    {
                        agreementId: 'a1',
                        containsPublicDomain: false,
                        containsSample: false,
                        hasArrangerContribution: true,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                        split: '69.89',
                        hasMusicContribution: false,
                        hasLyricsContribution: false,
                    },
                    {
                        agreementId: 'a2',
                        containsPublicDomain: false,
                        containsSample: false,
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                        split: '30.11',
                        hasMusicContribution: true,
                        hasArrangerContribution: false,
                        hasLyricsContribution: false,
                    },
                ],
                label: {
                    uuid: orchardLabelsMock[0].uuid,
                },
            },
        });
    });

    test('allows editing a submitted song', async () => {
        const composition = {
            id: '8b4df8a5-aa35-41e6-991f-f482493a4ca9',
            title: 'Power',
            alternateTitles: [],
            agreements: [
                {
                    agreement: {
                        id: 'a1',
                        controlled: true,
                        songWriter: {
                            id: '1',
                            pro: null,
                            ipi: null,
                        },
                    },
                    label: {
                        uuid: orchardLabelsMock[0].uuid,
                    },
                    split: '100',
                    hasMusicContribution: true,
                    hasLyricsContribution: false,
                },
            ],
            label: {
                id: {
                    subaccountId: 0,
                    vendorId: 2021,
                },
            },
            associatedGlobalSoundRecordings: [
                {
                    id: '145',
                    name: 'Bpot',
                    isrc: '12312',
                    globalParticipants: [],
                },
            ],
            draft: false,
        };
        locationPathnameMock = songUrl(composition.id);

        const { container } = renderComponent({ composition });
        await waitForLoaded();

        expect(screen.getByLabelText('Song Title')).not.toBeDisabled();
        expect(screen.getByText('Add Alternate Song Title')).toBeTruthy();
        expect(screen.getByLabelText('ISWC')).not.toBeDisabled();

        expect(
            container.querySelector('.LabelSearchDropdown .Select--is-disabled')
        ).toBeTruthy();
        expect(
            container.querySelector(
                '.NewSong-songwriters-name.Select--is-disabled'
            )
        ).toBeFalsy();
        expect(
            container.querySelector('.NewSong-songwriters-ownership input')
        ).not.toBeDisabled();
        expect(
            container.querySelectorAll(
                '.NewSong-songwriters-contribution input'
            )[0]
        ).not.toBeDisabled();
        expect(screen.getByText('Add Co-Writer')).toBeTruthy();

        expect(container.querySelectorAll('.CloseGlyphIcon')).toHaveLength(2);
    });

    test('shows a warning about potentially duplicate composition based on matching song titles', async () => {
        renderComponent();
        await waitForLoaded();

        await userEvent.clear(screen.getByLabelText('Song Title'));
        await userEvent.type(screen.getByLabelText('Song Title'), 'test title');

        // Wait  for debounced callback
        await wait(600);
        expect(
            screen.getAllByText(
                "We've found an existing song with the same title in your catalog. Would you like to edit it instead?"
            ).length
        ).toBe(1);
    });

    test('can not submit if associated ISRC already exists on another composition', async () => {
        const isrc = 'QMFMF1565547';
        const compositionId = '0bc6f6ff-529e-481d-9d73-47c4f4d337c0';
        const mockIsrcEntities = {
            isrc,
            compositions: {
                compositions: [
                    { id: compositionId, vendor: { vendorId: 7123 } },
                ],
            },
        };

        jest.spyOn(isrcEntities, 'useIsrcEntitiesFetcher').mockReturnValue(
            jest.fn().mockResolvedValue(mockIsrcEntities)
        );
        jest.spyOn(helpersMock, 'useGetClashedCompositions').mockReturnValue(
            'clashed-composition-id'
        );

        renderComponent();
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            isrc
        );
        await waitForLoaded();
        await userEvent.click(screen.getByText(isrc));

        await userEvent.click(screen.getByText('Submit'));
        await waitFor(() => {
            expect(
                screen.getAllByText(
                    'We have found an existing song already associated with this ISRC. Would you like to edit that song instead?'
                ).length
            ).toBe(2);
        });
    });

    test('can make draft if associated ISRC already exists on another composition', async () => {
        const isrc = 'QMFMF1565547';
        const compositionId = '0bc6f6ff-529e-481d-9d73-47c4f4d337c0';
        const mockIsrcEntities = {
            isrc,
            compositions: {
                compositions: [
                    { id: compositionId, vendor: { vendorId: 7123 } },
                ],
            },
        };

        jest.spyOn(isrcEntities, 'useIsrcEntitiesFetcher').mockReturnValue(
            jest.fn().mockResolvedValue(mockIsrcEntities)
        );
        jest.spyOn(helpersMock, 'useGetClashedCompositions').mockReturnValue(
            'clashed-composition-id'
        );

        renderComponent();
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            isrc
        );
        await waitForLoaded();
        await userEvent.click(screen.getByText(isrc));

        await userEvent.click(screen.getByText('Save Draft'));

        expect(Segment.trackEvent).toHaveBeenCalledTimes(1);
    });

    test('checks every recording for an ISRC clash when several are added at once', async () => {
        const owsTrack = (isrc: string, trackName: string) => ({
            analytics: { streams: { aggregate: { allTime: null } } },
            isrc,
            labelSoundRecording: {
                id: `lsr-${isrc}`,
                globalSoundRecording: { id: `gsr-${isrc}` },
            },
            participations: [
                {
                    participant: { name: 'Kriss Norman' },
                    participatedAs: 'performer',
                },
            ],
            product: {
                imageLocation: '',
                productName: trackName,
                releaseDate: '2016-06-10',
                upc: '190374887809',
                status: 'submitted',
            },
            trackName,
        });

        jest.spyOn(trackSearch, 'useLazyTrackSearch').mockReturnValue([
            jest.fn(),
            {
                loading: false,
                called: true,
                error: undefined,
                songs: [
                    owsTrack('AAA111111111', 'Track One'),
                    owsTrack('BBB222222222', 'Track Two'),
                    owsTrack('CCC333333333', 'Track Three'),
                ],
            },
        ]);
        jest.spyOn(
            spotifyTrackSearch,
            'useLazySpotifyTrackSearch'
        ).mockReturnValue([
            jest.fn(),
            { loading: false, called: true, error: undefined, data: [] },
        ]);

        // only the last of the three recordings clashes
        const fetchIsrcEntities = jest.fn(
            async (isrc: string) =>
                await Promise.resolve(
                    isrc === 'CCC333333333'
                        ? {
                              isrc,
                              compositions: {
                                  compositions: [
                                      {
                                          id: 'clashed-composition-id',
                                          vendor: {
                                              vendorId:
                                                  orchardLabelsMock[0].id
                                                      .vendorId,
                                          },
                                      },
                                  ],
                              },
                          }
                        : null
                )
        );
        jest.spyOn(isrcEntities, 'useIsrcEntitiesFetcher').mockReturnValue(
            fetchIsrcEntities
        );

        renderComponent(
            {},
            {
                identity: {
                    features: { [FEATURE_FLAGS.ADMIN_UX_IMPROVEMENTS]: true },
                },
            }
        );
        await waitForLoaded();

        await userEvent.type(
            screen.getByPlaceholderText(searchPlaceholder),
            'track'
        );
        await waitForLoaded();

        await userEvent.click(screen.getByText('Select All'));
        await userEvent.click(screen.getByText('Add Selected (3)'));

        await waitFor(() =>
            expect(fetchIsrcEntities).toHaveBeenCalledWith('AAA111111111')
        );
        expect(fetchIsrcEntities).toHaveBeenCalledWith('BBB222222222');
        expect(fetchIsrcEntities).toHaveBeenCalledWith('CCC333333333');

        // the clash sits on the third recording, so it can only be found if all
        // three were looked up
        await waitFor(() =>
            expect(
                screen.getAllByText(
                    'We have found an existing song already associated with this ISRC. Would you like to edit that song instead?'
                ).length
            ).toBeGreaterThan(0)
        );
    });

    test('common Label Picker should be rendered at the header', async () => {
        const { container } = renderComponent();

        await waitForLoaded();

        const headerLabelPicker = container.querySelector(
            '.NewSong .AppHeader .HeaderLabelPicker .LabelSearchDropdown'
        );
        expect(headerLabelPicker).toBeTruthy();

        const pageTitle = container.querySelector(
            '.NewSong .AppHeader .breadcrumb'
        );
        expect(pageTitle).toBeFalsy();
    });

    test('Song ID input is shown for employees', async () => {
        const composition = {
            id: '8b4df8a5-aa35-41e6-991f-f482493a4ca9',
            title: 'Power',
            alternateTitles: [],
            draft: false,
            pubSongId: 123,
            agreements: [],
        };

        locationPathnameMock = songUrl(composition.id);

        const { container } = renderComponent(
            { composition },
            { identity: { isEmployee: true } }
        );
        await waitForLoaded();

        const songIdInput = container.querySelector(
            'input#songId'
        ) as HTMLInputElement;

        expect(songIdInput).toBeInTheDocument();
        expect(songIdInput).toBeDisabled();
        expect(songIdInput?.value).toEqual(composition.pubSongId.toString());
    });

    test('Song ID input is not shown for non-employees', async () => {
        const composition = {
            id: '8b4df8a5-aa35-41e6-991f-f482493a4ca9',
            title: 'Power',
            alternateTitles: [],
            draft: false,
            pubSongId: 123,
            agreements: [],
        };

        locationPathnameMock = songUrl(composition.id);

        const { container } = renderComponent(
            { composition },
            { identity: { isEmployee: false } }
        );
        await waitForLoaded();

        const songIdInput = container.querySelector(
            'input#songId'
        ) as HTMLInputElement;
        expect(songIdInput).not.toBeInTheDocument();
    });

    it('cannot update song when LIMIT_MODIFY_ACCESS FF is enabled', async () => {
        const composition = {
            id: '8b4df8a5-aa35-41e6-991f-f482493a4ca9',
            title: 'Power',
            alternateTitles: [],
            draft: false,
            agreements: [],
        };

        jest.spyOn(featuresMock, 'useLimitModifyAccessFF').mockReturnValue(
            true
        );

        renderComponent({ composition });
        await waitForLoaded();

        expect(screen.getByText('Update')).toBeDisabled();
    });

    it('cannot submit song when LIMIT_MODIFY_ACCESS FF is enabled', async () => {
        jest.spyOn(featuresMock, 'useLimitModifyAccessFF').mockReturnValue(
            true
        );

        renderComponent();

        await waitForLoaded();

        expect(screen.getByText('Save Draft')).toBeDisabled();
        expect(screen.getByText('Submit')).toBeDisabled();
    });

    describe('can save draft', () => {
        const cases = [
            {
                name: 'GSR',
                template: associatedSoundRecording,
                expected: {
                    variables: {
                        title: 'cool title',
                        draft: true,
                        alternateTitles: [],
                        iswc: null,
                        associatedGlobalSoundRecordings: [
                            '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                        ],
                        associatedLabelSoundRecordings: [],
                        agreements: [],
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                        containsPublicDomain: false,
                        containsSample: false,
                    },
                },
            },
            {
                name: 'LSR',
                template: associatedSoundRecordingWithLsr,
                expected: {
                    variables: {
                        title: 'cool title',
                        draft: true,
                        alternateTitles: [],
                        iswc: null,
                        associatedGlobalSoundRecordings: [],
                        associatedLabelSoundRecordings: [
                            '0bc6f6ff-529e-481d-9d73-47c4f4d337c0',
                        ],
                        agreements: [],
                        label: {
                            uuid: orchardLabelsMock[0].uuid,
                        },
                        containsPublicDomain: false,
                        containsSample: false,
                    },
                },
            },
        ];

        test.each`
            name             | template             | expected
            ${cases[0].name} | ${cases[0].template} | ${cases[0].expected}
            ${cases[1].name} | ${cases[1].template} | ${cases[1].expected}
        `('with associated $name', async ({ template, expected }) => {
            renderComponent({ associatedSoundRecording: template });
            await waitForLoaded();

            await userEvent.clear(screen.getByLabelText('Song Title'));
            await userEvent.type(
                screen.getByLabelText('Song Title'),
                'cool title'
            );
            await userEvent.click(screen.getByText('Save Draft'));

            expect(Segment.trackEvent).toHaveBeenCalledWith(
                'Click - Create Song',
                {
                    category: 'New Song',
                    alternateTitles: [],
                    iswc: '',
                    title: 'cool title',
                }
            );

            expect(createMock).toHaveBeenCalledWith(expected);
        });
    });
});
