import React from 'react';
import {
    getByText,
    getByTestId,
    getByRole,
    queryByText,
    screen,
    fireEvent,
    waitFor,
    waitForElementToBeRemoved
} from '@testing-library/react';
import { Switch, Route } from 'react-router-dom';
import selectEvent from 'react-select-event';
import { renderComponent } from 'lib/test-helpers/render-helper';
import SplitsSetupPage from 'src/pages/splits-setup-page/splits-setup-page';
import { createMock as createProductQueryMock } from 'src/queries/product';
import { createCollaboratorSearchQueryMock } from 'src/queries/collaborator-search';
import { productWithArtists as productFixture } from 'src/__fixtures__/product';
import { tracksWithSplits as tracksFixture } from 'src/__fixtures__/tracks';
import { CollaboratorSplitRateType, CollaboratorType, SubaccountSplitType } from 'src/__definitions__/globalTypes';
import { createSaveCollaboratorSplitsMutationMock } from 'src/mutations/save-collaborator-splits';
import { createCreateCollaboratorMutationMock } from 'src/mutations/create-collaborator';
import { createVendorCurrencyQueryMock } from 'src/queries/vendor-currency';
import { vendorCurrency } from 'src/__fixtures__/vendor-currency';
import { createNewStore } from 'src/store/store';
import { createAllProductsSearchQueryMock } from 'src/queries/all-products-search';
import { PRODUCT_SEARCH_RESULTS_LIMIT, NUMBER_FORMATS } from 'src/constants';
import { products as allProductSearchProductsFixture } from 'src/__fixtures__/allProductSearch';

// Nodejs (and Jest by extension) doesn't load more than one locale for Intl
// so it doesn't behave as we expect during testing. Below is a util for mocking
// the Intl.NumberFormat class for formatting localised decimals
jest.spyOn(Intl, 'NumberFormat').mockImplementation((locale) => ({
    format(value: number) {
        const separator = locale === 'en' ? '.' : ',';
        const truncatedValue = (+(value * 100).toFixed(2)).toString();
        const localisedPercentage = `${truncatedValue.split('.').join(separator)}%`;
        return localisedPercentage;
    }
} as any)); // eslint-disable-line

const mocks = [
    createAllProductsSearchQueryMock(
        { term: '', limit: PRODUCT_SEARCH_RESULTS_LIMIT },
        {
            allProductsSearch: {
                totalCount: 3,
                products: allProductSearchProductsFixture,
                __typename: 'ProductSearchResults',
            },
        }
    ),
    createProductQueryMock(
        { productId: '123' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.GROSS,
                    commissionOverride: 0,
                },
                tracks: tracksFixture.slice(0, 2)
            },
        }
    ),
    // With track with tracks with same ISRC
    createProductQueryMock(
        { productId: '124' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.GROSS,
                    commissionOverride: 0,
                },
                tracks: tracksFixture.slice(0, 3)
            },
        }
    ),
    // Product with the same rate types
    createProductQueryMock(
        { productId: '126' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.NET,
                    commissionOverride: 0,
                },
                tracks: tracksFixture.slice(0, 2)
            },
        }
    ),
    // Product with no tracks
    createProductQueryMock(
        { productId: '125' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.GROSS,
                    commissionOverride: 0,
                },
                tracks: [],
            },
        }
    ),
    createProductQueryMock(
        { productId: '127' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.GROSS,
                    commissionOverride: 0,
                },
                tracks: [tracksFixture[3]],
            },
        }
    ),
    // Product with multiple volumes
    createProductQueryMock(
        { productId: '666' },
        {
            product: {
                ...productFixture,
                subaccount: {
                    __typename: 'OrchardSubaccount',
                    id: '999',
                    subaccountName: 'Three Hundred and Thirty Three Tigers',
                    subaccountSplitType: SubaccountSplitType.GROSS,
                    commissionOverride: 0,
                },
                tracks: [
                    ...tracksFixture.slice(0, 2),
                    {
                        ...JSON.parse(JSON.stringify(tracksFixture[2])),
                        volumeNumber: 2
                    }
                ]
            },
        }
    ),
    createCollaboratorSearchQueryMock(
        { searchTerm: 'hello' },
        {
            collaboratorSearch: {
                collaborators: [
                    {
                        __typename: 'Collaborator',
                        name: 'My Guy',
                        id: 1,
                        collaboratorType: CollaboratorType.COLLABORATOR,
                        subaccountId: 1234,
                    },
                    {
                        __typename: 'Collaborator',
                        name: 'Roo Dog',
                        id: 555,
                        collaboratorType: CollaboratorType.COLLABORATOR,
                        subaccountId: 1234,
                    },
                ],
                __typename: 'CollaboratorResults',
            },
        }
    ),
    // Adding new collaborator split at product level
    createSaveCollaboratorSplitsMutationMock(
        {
            create: [
                {
                    splitTypeId: 2,
                    identifier: '12345',
                    splitRate: 0.55,
                    collaboratorId: 555,
                    rateType: CollaboratorSplitRateType.GROSS,
                    productId: 123,
                },
                {
                    splitTypeId: 2,
                    identifier: '12346',
                    splitRate: 0.55,
                    collaboratorId: 555,
                    rateType: CollaboratorSplitRateType.GROSS,
                    productId: 123,
                }
            ],
        },
        {
            saveCollaboratorSplits: {
                created: [
                    {
                        id: 8181,
                        identifier: '12345',
                        splitRate: 0.55,
                        rateType: CollaboratorSplitRateType.GROSS,
                        collaborator: {
                            id: 555,
                            name: 'Roo Dog',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                    {
                        id: 8182,
                        identifier: '12346',
                        splitRate: 0.55,
                        rateType: CollaboratorSplitRateType.GROSS,
                        collaborator: {
                            id: 555,
                            name: 'Roo Dog',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                ],
                updated: [],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Applying splits with same rate type
    createSaveCollaboratorSplitsMutationMock(
        {
            create: [
                {
                    splitTypeId: 2,
                    identifier: '12345',
                    splitRate: 0.55,
                    collaboratorId: 555,
                    rateType: CollaboratorSplitRateType.NET,
                    productId: 123,
                },
                {
                    splitTypeId: 2,
                    identifier: '12346',
                    splitRate: 0.55,
                    collaboratorId: 555,
                    rateType: CollaboratorSplitRateType.NET,
                    productId: 123,
                }
            ],
        },
        {
            saveCollaboratorSplits: {
                created: [
                    {
                        id: 8181,
                        identifier: '12345',
                        splitRate: 0.55,
                        rateType: CollaboratorSplitRateType.NET,
                        collaborator: {
                            id: 555,
                            name: 'Roo Dog',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                    {
                        id: 8182,
                        identifier: '12346',
                        splitRate: 0.55,
                        rateType: CollaboratorSplitRateType.NET,
                        collaborator: {
                            id: 555,
                            name: 'Roo Dog',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                ],
                updated: [],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Adding new subaccount split at product level
    createSaveCollaboratorSplitsMutationMock(
        {
            create: [
                {
                    splitTypeId: 2,
                    identifier: '12345',
                    splitRate: 0.1331,
                    collaboratorId: 5678,
                    rateType: CollaboratorSplitRateType.GROSS,
                    productId: 123,
                },
                {
                    splitTypeId: 2,
                    identifier: '12346',
                    splitRate: 0.1331,
                    collaboratorId: 5678,
                    rateType: CollaboratorSplitRateType.GROSS,
                    productId: 123,
                }
            ],
        },
        {
            saveCollaboratorSplits: {
                created: [
                    {
                        id: 9001,
                        identifier: '12345',
                        splitRate: 0.1331,
                        rateType: CollaboratorSplitRateType.GROSS,
                        collaborator: {
                            id: 5678,
                            name: 'Three Hundred and Thirty Three Tigers',
                            collaboratorType: CollaboratorType.SUBACCOUNT,
                            subaccountId: 999,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                    {
                        id: 9002,
                        identifier: '12346',
                        splitRate: 0.1331,
                        rateType: CollaboratorSplitRateType.GROSS,
                        collaborator: {
                            id: 5678,
                            name: 'Three Hundred and Thirty Three Tigers',
                            collaboratorType: CollaboratorType.SUBACCOUNT,
                            subaccountId: 999,
                            __typename: 'Collaborator'
                        },
                        __typename: 'CollaboratorSplit'
                    },
                ],
                updated: [],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Updating collaborator split at product level
    createSaveCollaboratorSplitsMutationMock(
        {
            update: [
                {
                    splitRate: 0.72, collaboratorId: 2, rateType: CollaboratorSplitRateType.GROSS, id: 456
                },
                {
                    splitRate: 0.72, collaboratorId: 2, rateType: CollaboratorSplitRateType.GROSS, id: 190
                }
            ]
        },
        {
            saveCollaboratorSplits: {
                created: [],
                updated: [
                    {
                        splitRate: 0.72,
                        identifier: '12345',
                        collaborator: {
                            id: 2,
                            name: 'Fly Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        rateType: CollaboratorSplitRateType.GROSS,
                        id: 456,
                        __typename: 'CollaboratorSplit' as const,
                    },
                    {
                        splitRate: 0.72,
                        identifier: '12346',
                        collaborator: {
                            id: 2,
                            name: 'Fly Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        rateType: CollaboratorSplitRateType.GROSS,
                        id: 190,
                        __typename: 'CollaboratorSplit' as const,
                    },
                ],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Updating collaborator split at track level
    createSaveCollaboratorSplitsMutationMock(
        {
            update: [{
                id: 190,
                collaboratorId: 2,
                rateType: CollaboratorSplitRateType.NET,
                splitRate: 0.72
            }] },
        {
            saveCollaboratorSplits: {
                created: [],
                updated: [
                    {
                        splitRate: 0.72,
                        identifier: '12346',
                        collaborator: {
                            id: 2,
                            name: 'Fly Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        rateType: CollaboratorSplitRateType.NET,
                        id: 190,
                        __typename: 'CollaboratorSplit' as const,
                    },
                ],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Updating prorated product split rate type
    createSaveCollaboratorSplitsMutationMock(
        {
            update: [
                { id: 123, collaboratorId: 1, rateType: CollaboratorSplitRateType.GROSS },
                { id: 189, collaboratorId: 1, rateType: CollaboratorSplitRateType.GROSS },
            ]
        },
        {
            saveCollaboratorSplits: {
                created: [],
                updated: [
                    {
                        splitRate: 0.4,
                        identifier: '12345',
                        collaborator: {
                            id: 1,
                            name: 'My Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        rateType: CollaboratorSplitRateType.GROSS,
                        id: 123,
                        __typename: 'CollaboratorSplit' as const,
                    },
                    {
                        splitRate: 0.5,
                        identifier: '12346',
                        collaborator: {
                            id: 1,
                            name: 'My Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        rateType: CollaboratorSplitRateType.GROSS,
                        id: 189,
                        __typename: 'CollaboratorSplit' as const,
                    },
                ],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Deleting split at product level
    createSaveCollaboratorSplitsMutationMock(
        { delete: [{ id: 456 }, { id: 190 }] },
        {
            saveCollaboratorSplits: {
                created: [],
                updated: [],
                deleted: [456, 190],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Deleting split at track level
    createSaveCollaboratorSplitsMutationMock(
        { delete: [{ id: 190 }] },
        {
            saveCollaboratorSplits: {
                created: [],
                updated: [],
                deleted: [190],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    // Applying splits to track with same ISRC
    createSaveCollaboratorSplitsMutationMock(
        {
            create: [
                { identifier: '34357588',
                    collaboratorId: 1,
                    splitRate: 0.5,
                    rateType: CollaboratorSplitRateType.NET,
                    splitTypeId: 2,
                    productId: 3256822 },

                { identifier: '34357588',
                    collaboratorId: 2,
                    splitRate: 0.5,
                    rateType: CollaboratorSplitRateType.NET,
                    splitTypeId: 2,
                    productId: 3256822 }
            ],
            update: [],
            delete: []
        },
        {
            saveCollaboratorSplits: {
                created: [
                    {
                        id: 9001,
                        identifier: '34357588',
                        collaborator: {
                            id: 1,
                            name: 'My Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        splitRate: 0.5,
                        rateType: CollaboratorSplitRateType.NET,
                        __typename: 'CollaboratorSplit' as const,
                    },
                    {
                        id: 9002,
                        identifier: '34357588',
                        collaborator: {
                            id: 2,
                            name: 'Fly Guy',
                            collaboratorType: CollaboratorType.COLLABORATOR,
                            subaccountId: null,
                            __typename: 'Collaborator' as const,
                        },
                        splitRate: 0.5,
                        rateType: CollaboratorSplitRateType.NET,
                        __typename: 'CollaboratorSplit' as const,
                    }
                ],
                updated: [],
                deleted: [],
                __typename: 'SaveCollaboratorSplitsResult'
            }
        }
    ),
    createVendorCurrencyQueryMock({ vendorCurrency }),
    // Creating subaccount collaborator
    createCreateCollaboratorMutationMock(
        {
            name: 'Three Hundred and Thirty Three Tigers',
            subaccountId: 999,
            currency: 'USD',
            collaboratorType: CollaboratorType.SUBACCOUNT
        },
        {
            createCollaborator: {
                __typename: 'Collaborator',
                name: 'Three Hundred and Thirty Three Tigers',
                subaccountId: 999,
                id: 5678,
                collaboratorType: CollaboratorType.SUBACCOUNT,
            }
        }
    ),
    // Creating new collaborator
    createCreateCollaboratorMutationMock(
        {
            name: 'The Lawrence Arms',
            participantId: 12345,
            subaccountId: 999,
            currency: 'USD',
            collaboratorType: CollaboratorType.COLLABORATOR
        },
        {
            createCollaborator: {
                __typename: 'Collaborator',
                name: 'The Lawrence Arms',
                subaccountId: 999,
                id: 97531,
                collaboratorType: CollaboratorType.COLLABORATOR
            }
        }
    )
];

const SplitSetupPageRouteComponent = () => (
    <Switch>
        <Route
            exact
            path="/accounting/collaborators/view/:productId"
            component={ SplitsSetupPage }
        />
        <Route
            exact
            path="/accounting/collaborators/view/:productId/track/:tuid"
            component={ SplitsSetupPage }
        />
    </Switch>
);

describe('<SplitsSetupPage>', () => {
    test('renders loading state', async () => {
        const props = {
            match: {
                params: {
                    productId: '123'
                }
            }
        };

        renderComponent({ Component: SplitsSetupPage, props, mocks });

        const loader = document.querySelector('.LoaderWrapper');
        expect(loader).toBeInTheDocument();

        await waitFor(() => {
            expect(loader).not.toBeInTheDocument();
        });
    });

    test('shows product information', async () => {
        const props = {
            match: {
                params: {
                    productId: '123'
                }
            }
        };

        renderComponent({ Component: SplitsSetupPage, props, mocks });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const productInfo = document.querySelector('.ProductInfo') as HTMLElement;

        expect(getByText(productInfo, 'Test 1 Product')).toBeInTheDocument();
        expect(getByText(productInfo, 'Mr Cool Guy, Way Cool Artist')).toBeInTheDocument();
    });

    test('track selection', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        // Initially URL doesn't include track and only product splits are shown
        expect(window.location.pathname).toBe('/accounting/collaborators/view/123');
        expect(document.querySelectorAll('.SplitsList')).toHaveLength(1);

        const tracksList = document.querySelector('.TracksList') as HTMLElement;

        // Select track
        const trackOption = getByText(tracksList, 'Track 2');
        fireEvent.click(trackOption);

        // URL includes track and track splits are shown
        expect(window.location.pathname).toBe('/accounting/collaborators/view/123/track/12346');
        expect(document.querySelectorAll('.SplitsList')).toHaveLength(2);
    });

    test('opening and cancelling split editor', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Click product level add collaborator button
        let addCollaboratorButton = getByText(productSplitsList, 'labels.addCollaborator');
        fireEvent.click(addCollaboratorButton);

        // Button disappears
        expect(addCollaboratorButton).not.toBeInTheDocument();

        // Split editor appears
        const productSplitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;
        expect(productSplitEditor).toBeInTheDocument();

        // Click cancel button
        fireEvent.click(getByTestId(productSplitEditor, 'cancel-split-edit-button'));

        addCollaboratorButton = getByText(productSplitsList, 'labels.addCollaborator');

        // Split editor disappears and add collaborator button reappears
        expect(productSplitEditor).not.toBeInTheDocument();
        expect(addCollaboratorButton).toBeInTheDocument();
    });

    test('adding a collaborator split at product level with mixed rate types', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Initially 2 existing splits are shown
        expect(productSplitsList.querySelectorAll('.SplitsList-split-row')).toHaveLength(2);

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));
        const productSplitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;

        // Search for collaborator
        const collaboratorSelect = getByText(productSplitEditor, 'messages.searchCollaborator').closest('.Select') as HTMLElement;
        const collaboratorSelectInput = getByRole(collaboratorSelect, 'textbox');
        fireEvent.change(collaboratorSelectInput, { target: { value: 'hello' } });

        // Wait for collaborator results to load
        await waitFor(() => {
            expect(collaboratorSelect.querySelector('.Select__loading-indicator')).toBeFalsy();
        });

        // Select collaborator
        fireEvent.click(getByText(collaboratorSelect, 'Roo Dog'));

        // Select rate type
        fireEvent.click(getByText(productSplitEditor, 'labels.gross'));

        // Enter percentage rate value
        fireEvent.change(getByTestId(productSplitEditor, 'percentage-rate-input'), { target: { value: '55' } });

        // Click save button
        fireEvent.click(getByTestId(productSplitEditor, 'save-split-edit-button'));

        // Wait for split editor to disappear
        await waitFor(() => {
            expect(productSplitEditor).not.toBeInTheDocument();
        });

        // Newly added split is shown in the list
        expect(productSplitsList.querySelectorAll('.SplitsList-split-row')).toHaveLength(3);
    });
    test('adding a collaborator split at product level with the same rate types', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/126/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Initially 2 existing splits are shown
        expect(productSplitsList.querySelectorAll('.SplitsList-split-row')).toHaveLength(2);

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));
        const productSplitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;

        // Search for collaborator
        const collaboratorSelect = getByText(productSplitEditor, 'messages.searchCollaborator').closest('.Select') as HTMLElement;
        const collaboratorSelectInput = getByRole(collaboratorSelect, 'textbox');
        fireEvent.change(collaboratorSelectInput, { target: { value: 'hello' } });

        // Wait for collaborator results to load
        await waitFor(() => {
            expect(collaboratorSelect.querySelector('.Select__loading-indicator')).toBeFalsy();
        });

        // Select collaborator
        fireEvent.click(getByText(collaboratorSelect, 'Roo Dog'));

        // Select rate type
        fireEvent.click(getByText(productSplitEditor, 'labels.net'));

        // Enter percentage rate value
        fireEvent.change(getByTestId(productSplitEditor, 'percentage-rate-input'), { target: { value: '55' } });

        // Click save button
        fireEvent.click(getByTestId(productSplitEditor, 'save-split-edit-button'));

        // Wait for split editor to disappear
        await waitFor(() => {
            expect(productSplitEditor).not.toBeInTheDocument();
        });

        // Newly added split is shown in the list
        expect(productSplitsList.querySelectorAll('.SplitsList-split-row')).toHaveLength(3);
    });

    test('adding a subaccount split at product level', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Click add subaccount button
        const addSubaccountButton = getByText(productSplitsList, 'labels.addSubaccount (Three Hundred and Thirty Three Tigers)');
        fireEvent.click(addSubaccountButton);

        // Wait for button to disappear
        await waitFor(() => {
            expect(queryByText(productSplitsList, 'labels.addSubaccount (Three Hundred and Thirty Three Tigers)')).toBe(null);
        });

        const productSplitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;

        // Subaccount name should be pre-filled and not editable
        const collaboratorInput = productSplitEditor.querySelector('.SplitEditor-collaborator-input-container input') as HTMLInputElement;
        expect(collaboratorInput.value).toBe('Three Hundred and Thirty Three Tigers');
        expect(collaboratorInput.disabled).toBe(true);

        // Select rate type
        fireEvent.click(getByText(productSplitEditor, 'labels.gross'));

        // Enter percentage rate value
        fireEvent.change(getByTestId(productSplitEditor, 'percentage-rate-input'), { target: { value: '13.31' } });

        // Click save button
        fireEvent.click(getByTestId(productSplitEditor, 'save-split-edit-button'));

        // Wait for split editor to disappear
        await waitFor(() => {
            expect(productSplitEditor).not.toBeInTheDocument();
        });

        // Add subaccount button should not be shown
        expect(queryByText(productSplitsList, 'labels.addSubaccount (Three Hundred and Thirty Three Tigers)')).toBe(null);

        // Correct details are shown in subaccount split row
        const [productSubaccountSplitRow] = Array.from<HTMLElement>(productSplitsList.querySelectorAll('.SplitsList-split-row--editable'));
        expect(productSubaccountSplitRow.querySelector('.SplitsList-collaborator-name') as HTMLElement)
            .toHaveTextContent('Three Hundred and Thirty Three Tigers (labels.subaccount)');
        expect(productSubaccountSplitRow.querySelector('.SplitsList-split-type') as HTMLElement)
            .toHaveTextContent('GROSS');
        expect(productSubaccountSplitRow.querySelector('.SplitsList-split-rate') as HTMLElement)
            .toHaveTextContent('13.31%');
    });

    test('editing an existing split at product level', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        let splitRow = getByText(productSplitsList, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;

        // Click split edit button
        fireEvent.click(getByTestId(splitRow, 'edit-split-button'));

        // Split row is replaced with editor
        expect(splitRow).not.toBeInTheDocument();
        const splitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;
        expect(splitEditor).toBeTruthy();

        // Correct details are prepopulated
        const collaboratorInput = splitEditor.querySelector('.SplitEditor-collaborator-input-container input') as HTMLInputElement;
        const percentageRateInput = getByTestId(splitEditor, 'percentage-rate-input') as HTMLInputElement;
        expect(collaboratorInput.value).toBe('Fly Guy');
        expect(collaboratorInput.disabled).toBe(true);
        expect(getByText(splitEditor, 'labels.net')).toHaveClass('active');
        expect(percentageRateInput.value).toBe('50.00');

        // Change split rate type and value
        fireEvent.click(getByText(splitEditor, 'labels.gross'));
        fireEvent.change(percentageRateInput, { target: { value: '72' } });

        // Click save button
        fireEvent.click(getByTestId(splitEditor, 'save-split-edit-button'));

        // Wait for split editor to disappear
        await waitFor(() => {
            expect(splitEditor).not.toBeInTheDocument();
        });

        // Updated values are shown in split row
        splitRow = getByText(productSplitsList, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;
        expect(splitRow.querySelector('.SplitsList-split-type') as HTMLElement).toHaveTextContent('GROSS');
        expect(splitRow.querySelector('.SplitsList-split-rate') as HTMLElement).toHaveTextContent('72%');
    });

    test('editing an existing split at track level', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitForElementToBeRemoved(document.querySelector('.LoaderWrapper'));

        const [, trackSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        let splitRow = getByText(trackSplitsList, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;

        // Click split edit button
        fireEvent.click(getByTestId(splitRow, 'edit-split-button'));

        // Split row is replaced with editor
        expect(splitRow).not.toBeInTheDocument();
        const splitEditor = trackSplitsList.querySelector('.SplitEditor') as HTMLElement;
        expect(splitEditor).toBeTruthy();

        // Correct details are prepopulated
        const collaboratorInput = splitEditor.querySelector('.SplitEditor-collaborator-input-container input') as HTMLInputElement;
        const percentageRateInput = getByTestId(splitEditor, 'percentage-rate-input') as HTMLInputElement;
        expect(collaboratorInput).toHaveValue('Fly Guy');
        expect(collaboratorInput).toBeDisabled();
        expect(percentageRateInput.value).toBe('50.00');

        const [netButton, grossButton] = ['labels.net', 'labels.gross'].map(label => getByText(splitEditor, label));
        [netButton, grossButton].forEach(button => { expect(button).toBeDisabled(); });
        expect(netButton).toHaveClass('active');
        expect(grossButton).not.toHaveClass('active');

        fireEvent.mouseOver(netButton);

        // Warning message appears
        const rateTypeWarningMessage = screen.getByText('messages.thisValueCanOnlyBeEditedForAllTracksOnThisProductWarning');
        expect(rateTypeWarningMessage).toBeInTheDocument();

        fireEvent.mouseOut(netButton);

        // Warning message disappears
        await waitForElementToBeRemoved(rateTypeWarningMessage);

        // Change value
        fireEvent.change(percentageRateInput, { target: { value: '72' } });

        // Click save button
        fireEvent.click(getByTestId(splitEditor, 'save-split-edit-button'));

        // Wait for split editor to disappear
        await waitForElementToBeRemoved(splitEditor);

        // Updated values are shown in split row
        splitRow = getByText(trackSplitsList, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;
        expect(splitRow.querySelector('.SplitsList-split-type') as HTMLElement).toHaveTextContent('NET');
        expect(splitRow.querySelector('.SplitsList-split-rate') as HTMLElement).toHaveTextContent('72%');
    });

    test('deleting a split at product level', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        const splitRow = getByText(productSplitsList, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;

        // Click delete split button
        const deleteSplitButton = getByTestId(splitRow, 'delete-split-button') as HTMLButtonElement;
        fireEvent.click(deleteSplitButton);

        // PopConfirm opens with warning text
        const popConfirm = document.querySelector('.PopConfirm') as HTMLElement;
        getByText(popConfirm, 'messages.thisWillDeleteSplitsOnAllTracksWarning');

        // Click PopConfirm delete button
        const popConfirmDeleteButton = getByText(popConfirm, 'labels.delete');
        fireEvent.click(popConfirmDeleteButton);

        // PopConfirm and split row should disappear
        await waitFor(() => {
            expect(popConfirm).not.toBeInTheDocument();
        });

        expect(splitRow).not.toBeInTheDocument();
    });

    test('deleting a split at track level', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [, trackCollaboratorEditor] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        const splitRow = getByText(trackCollaboratorEditor, 'Fly Guy').closest('.SplitsList-split-row') as HTMLElement;

        // Click delete split button
        const deleteSplitButton = getByTestId(splitRow, 'delete-split-button') as HTMLButtonElement;
        fireEvent.click(deleteSplitButton);

        // PopConfirm opens with warning text
        const popConfirm = document.querySelector('.PopConfirm') as HTMLElement;
        getByText(popConfirm, 'messages.thisWillDeleteSplitOnlyOnThisTrackWarning');

        // Click PopConfirm delete button
        const popConfirmDeleteButton = getByText(popConfirm, 'labels.delete');
        fireEvent.click(popConfirmDeleteButton);

        // PopConfirm and split row should disappear
        await waitFor(() => {
            expect(popConfirm).not.toBeInTheDocument();
        });

        expect(splitRow).not.toBeInTheDocument();
    });

    test('split edit validation', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Initially 2 existing splits are shown
        expect(productSplitsList.querySelectorAll('.SplitsList-split-row')).toHaveLength(2);

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));
        const splitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;

        // Click save button without selecting collaborator or entering rate
        const saveButton = getByTestId(splitEditor, 'save-split-edit-button') as HTMLElement;
        fireEvent.click(saveButton);

        // Both missing value error messages appear
        expect(queryByText(splitEditor, 'validation.selectACollaborator')).toBeTruthy();
        expect(queryByText(splitEditor, 'validation.enterAValue')).toBeTruthy();

        // Search for collaborator
        const collaboratorSelect = getByText(splitEditor, 'messages.searchCollaborator').closest('.Select') as HTMLElement;
        const collaboratorSelectInput = getByRole(collaboratorSelect, 'textbox');
        fireEvent.change(collaboratorSelectInput, { target: { value: 'hello' } });

        // Wait for collaborator results to load
        await waitFor(() => {
            expect(collaboratorSelect.querySelector('.Select__loading-indicator')).toBeFalsy();
        });

        // Select an unused collaborator
        fireEvent.change(collaboratorSelectInput, { target: { value: 'hello' } });
        fireEvent.click(getByText(collaboratorSelect, 'Roo Dog'));

        // Click save button
        fireEvent.click(saveButton);

        // Only percentage rate value error message appears
        expect(queryByText(splitEditor, 'validation.duplicateName')).toBeNull();
        expect(queryByText(splitEditor, 'validation.enterAValue')).toBeTruthy();
        expect(queryByText(splitEditor, 'validation.selectACollaborator')).toBeNull();

        // Select rate type
        fireEvent.click(getByText(splitEditor, 'labels.gross'));

        // Error messages disappear
        expect(queryByText(splitEditor, 'validation.duplicateName')).toBeNull();
        expect(queryByText(splitEditor, 'validation.selectACollaborator')).toBeNull();
        expect(queryByText(splitEditor, 'validation.enterAValue')).toBeNull();

        // Click save button
        fireEvent.click(saveButton);

        // Only percentage rate value error message appears
        expect(queryByText(splitEditor, 'validation.duplicateName')).toBeNull();
        expect(queryByText(splitEditor, 'validation.selectACollaborator')).toBeNull();
        expect(queryByText(splitEditor, 'validation.enterAValue')).toBeTruthy();

        // Enter percentage rate value
        fireEvent.change(getByTestId(splitEditor, 'percentage-rate-input'), { target: { value: '55' } });

        // Click save button
        fireEvent.click(getByTestId(splitEditor, 'save-split-edit-button'));

        // Split editor disappears
        await waitFor(() => {
            expect(splitEditor).not.toBeInTheDocument();
        });
    });

    test('apply splits to tracks with same ISRC', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/124/track/12347');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [, trackSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        const tracksWithSameIsrcButton = getByText(trackSplitsList, 'messages.tracksWithSameIsrc') as HTMLButtonElement;

        // Button is enabled initially
        expect(tracksWithSameIsrcButton.disabled).toBe(false);

        // Open track split editor
        fireEvent.click(getByText(trackSplitsList, 'labels.addCollaborator'));

        // Button is disabled while split editor is open
        expect(tracksWithSameIsrcButton.disabled).toBe(true);

        // Close track split editor
        fireEvent.click(getByTestId(trackSplitsList, 'cancel-split-edit-button'));

        // Click tracks with same ISRC button
        fireEvent.click(tracksWithSameIsrcButton);

        // Modal appears
        let bulkSplitsModal = screen.getByText('labels.applySameSplitAllTracks').closest('.ReactModal') as HTMLElement;

        // Click cancel button
        fireEvent.click(getByText(bulkSplitsModal, 'labels.noCancel'));

        // Modal disappears immediately
        expect(bulkSplitsModal).not.toBeInTheDocument();

        // Click tracks with same ISRC button again
        fireEvent.click(tracksWithSameIsrcButton);

        // Modal appears
        bulkSplitsModal = screen.getByText('labels.applySameSplitAllTracks').closest('.ReactModal') as HTMLElement;

        // Click apply button
        fireEvent.click(getByText(bulkSplitsModal, 'labels.yesApplyToAllTracksWithSameIsrc'));

        // Mutation completes and modal disappears
        await waitFor(() => {
            expect(bulkSplitsModal).not.toBeInTheDocument();
        });
    });

    test('creating a new collaborator', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123/track/12346');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));
        const productSplitEditor = productSplitsList.querySelector('.SplitEditor') as HTMLElement;

        const collaboratorSelect = getByText(productSplitEditor, 'messages.searchCollaborator').closest('.Select') as HTMLElement;
        const collaboratorSelectInput = getByRole(collaboratorSelect, 'textbox');

        // Open collaborator dropdown and select create new collaborator option
        selectEvent.openMenu(collaboratorSelectInput);
        await selectEvent.select(collaboratorSelectInput, '+ labels.createCollaborator');

        // Add collaborator modal appears
        const addCollaboratorModal = document.querySelector('.AddCollaboratorModal') as HTMLElement;
        expect(addCollaboratorModal).toBeInTheDocument();

        // Trigger simulated participant creation
        fireEvent.click(getByTestId(addCollaboratorModal, 'on-change-trigger'));
        await waitFor(() => { expect(queryByText(addCollaboratorModal, /The Lawrence Arms/)).toBeInTheDocument(); });

        // Click add button
        fireEvent.click(getByText(addCollaboratorModal, 'labels.add'));

        // Modal disappears
        await waitForElementToBeRemoved(addCollaboratorModal);

        // Newly created collaborator is selected
        expect(getByText(collaboratorSelect, 'The Lawrence Arms')).toBeInTheDocument();
    });

    test('clears split edit on unmount', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/124/track/12347');

        const store = createNewStore();

        const { unmount } = renderComponent({ Component: SplitSetupPageRouteComponent, mocks, store, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        let [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));

        // Split editor appears
        expect(productSplitsList.querySelector('.SplitEditor')).toBeInTheDocument();

        // Unmount and re-render component with same store
        unmount();
        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, store, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Split editor is not shown
        expect(productSplitsList.querySelector('.SplitEditor')).not.toBeInTheDocument();
    });

    test('clears split edit on navigating to different product', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/123');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [productSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        // Open split editor
        fireEvent.click(getByText(productSplitsList, 'labels.addCollaborator'));

        // Split editor appears
        expect(productSplitsList.querySelector('.SplitEditor')).toBeInTheDocument();

        // Open product search results
        const searchBar = screen.getByText('messages.productSearch').closest('.Select') as HTMLElement;
        const searchBarInput = getByRole(searchBar, 'textbox');
        selectEvent.openMenu(searchBarInput);

        // Select new product
        await selectEvent.select(searchBarInput, 'Cool product (Cool Version)');

        // Navigates to new product
        expect(window.location.pathname).toBe('/accounting/collaborators/view/124');

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        // Split editor is not shown
        expect(productSplitsList.querySelector('.SplitEditor')).not.toBeInTheDocument();
    });

    test('hovering over the info glyphs show the correct tooltips', async () => {
        const props = { match: { params: { productId: '123' } } };
        const component = renderComponent({
            Component: SplitsSetupPage,
            props,
            mocks
        });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).not.toBeInTheDocument();
        });

        const doTest = async (headingClass: string, text: string, exact = true) => {
            const heading = component.container.querySelector(headingClass)!!;
            const infoGlyph = heading.querySelector('.Help');
            fireEvent.mouseOver(infoGlyph);
            await component.findByText(text, { exact });
        };

        await doTest('.SplitsList-collaborator-heading', 'labels.not', false);
        await doTest('.TracksList-heading', 'messages.trackLevelTooltip');
    });

    describe('for US and non-US number formats', () => {
        const componentConfig = {
            Component: SplitsSetupPage,
            mocks,
            props: {
                match: { params: { productId: '124' } }
            },
        };

        test('the "." separator is used for US split rates', async () => {
            const component = renderComponent({
                ...componentConfig,
                identity: { numberFormat: NUMBER_FORMATS.US }
            });
            await waitFor(() => { expect(document.querySelector('.LoaderWrapper')).not.toBeInTheDocument(); });
            await component.findByText('46.67%');
        });

        test('the "," separator is used for non-US split rates', async () => {
            const component = renderComponent({
                ...componentConfig,
                identity: { numberFormat: NUMBER_FORMATS.EU }
            });
            await waitFor(() => { expect(document.querySelector('.LoaderWrapper')).not.toBeInTheDocument(); });
            await component.findByText('46,67%');
        });
    });

    test('product with no tracks', async () => {
        const props = { match: { params: { productId: '125' } } };

        renderComponent({
            Component: SplitsSetupPage,
            props,
            mocks
        });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).not.toBeInTheDocument();
        });

        expect(document.querySelector('.SplitsList')).not.toBeInTheDocument();
        expect(document.querySelector('.TracksList')).not.toBeInTheDocument();

        expect(document.querySelector('.ProductNoTracksPanel')).toBeInTheDocument();
    });

    describe('the disc column', () => {
        const componentConfig = {
            Component: SplitsSetupPage,
            mocks
        };

        test('is NOT visible when the products contains a single disc only', async () => {
            const component = renderComponent({
                ...componentConfig,
                props: { match: { params: { productId: '123' } } }
            });

            const discColumn = await component.findByText('labels.disc');
            expect(discColumn.classList.contains('TracksList-column--hidden')).toBeTruthy();
        });

        test('is visible when the product contains multiple discs', async () => {
            const component = renderComponent({
                ...componentConfig,
                props: { match: { params: { productId: '666' } } }
            });

            const discColumn = await component.findByText('labels.disc');
            expect(discColumn.classList.contains('TracksList-column--hidden')).not.toBeTruthy();
        });
    });

    test('already applied splits to tracks with same ISRC the message does not appear', async () => {
        window.history.replaceState({}, '', '/accounting/collaborators/view/127/track/12348');

        renderComponent({ Component: SplitSetupPageRouteComponent, mocks, addTypeName: true });

        await waitFor(() => {
            expect(document.querySelector('.LoaderWrapper')).toBeFalsy();
        });

        const [, trackSplitsList] = Array.from<HTMLElement>(document.querySelectorAll('.SplitsList'));

        const tracksWithSameIsrcButton = getByText(trackSplitsList, 'messages.tracksWithSameIsrc') as HTMLButtonElement;

        // Button is disabled while split editor is open
        expect(tracksWithSameIsrcButton.disabled).toBe(true);
    });
});
