import React from 'react';
import { ApolloQueryResult } from '@apollo/client';
import { screen, waitFor } from '@testing-library/react';
import { formatMessage, Segment } from '@theorchard/suite-frontend';
import { renderInAppContext } from '@theorchard/suite-testing';
import { waitForLoaded, userEvent } from 'lib/test/helpers';
import { FEATURE_FLAGS } from 'src/constants';
import * as createSongWriter from 'src/data/mutations/createSongWriter/createSongWriter';
import '@testing-library/jest-dom';
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 { labelsMock } from 'src/pages/songListPage/mocks';
import { Label } from 'src/types';
import * as applicationContext from 'src/utils/applicationContext';
import { songWritersMock, publishersMock } from '../mocks';
import RegisterNewSongwriter from '../registerNewSongWriter';

describe('<RegisterNewSongwriter>', () => {
    const defaultProps = {
        isOpen: true,
        onClose: jest.fn(),
        headerLabel: formatMessage('songWriterManager.registerNewSongWriter'),
        validated: true,
    };
    let createMock = jest.fn();
    const mockLabels = () => {
        const orchardLabelsMock: Label[] = labelsMock.data;
        jest.spyOn(orchardLabels, 'useOrchardLabels').mockReturnValue({
            data: orchardLabelsMock,
            loading: false,
            error: undefined,
        });
    };

    const mockSongWriters = () => {
        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({
            data: publishersMock,
            loading: false,
            error: undefined,
            refetch: async () =>
                await Promise.resolve(
                    {} as ApolloQueryResult<PublishingPublishersQuery>
                ),
            networkStatus: 7,
        });
    };

    const renderComponent = (customProps: Object) => {
        const props = {
            ...defaultProps,
            ...customProps,
        };
        return renderInAppContext(
            <RegisterNewSongwriter {...props} />,
            customProps
        );
    };
    beforeEach(() => {
        createMock = jest.fn();
        jest.spyOn(Segment, 'trackEvent');
        jest.spyOn(createSongWriter, 'useCreateSongWriter').mockReturnValue(
            createMock
        );
        jest.spyOn(applicationContext, 'useApplicationContext').mockReturnValue(
            {
                label: labelsMock.data[0],
                setLabel: jest.fn(),
                labelAlt: null,
                setLabelAlt: jest.fn(),
                pageFilters: { songPageSongWriters: null, brand: null },
                updatePageFilters: jest.fn(),
            }
        );
        mockLabels();
        mockSongWriters();
    });

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

    it('renders the RegisterNewSongwriter component', async () => {
        renderComponent({});
        await waitForLoaded();
        expect(
            screen.getByText('Register a New Songwriter')
        ).toBeInTheDocument();
        expect(screen.getByText('Label Account')).toBeInTheDocument();
    });

    it('can populate a songWriter', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('Jorja Smith'));
        expect(
            screen.getByText(
                'Are you collecting royalties on behalf of this writer?'
            )
        ).toBeInTheDocument();
        await userEvent.click(screen.getByText('Yes'));

        expect(
            screen.getByText(
                'Is this songwriter registered with a Performance Rights Organization?'
            )
        ).toBeInTheDocument();
        const proInputNo = document.getElementById('isRegisteredWithPro2');
        if (!proInputNo)
            throw new Error('Unable to find isRegisteredWithPro2 element');
        await userEvent.click(proInputNo);
        await userEvent.click(screen.getByText('Save'));
    });

    it('can save a songWriter with a new publishing company', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Billy Trodd'
        );
        await userEvent.click(screen.getAllByText('Yes')[0]);
        await userEvent.click(screen.getAllByText('Yes')[1]);

        await userEvent.click(screen.getByText("Enter Songwriter's PRO"));
        await userEvent.click(screen.getByText('ASCAP'));
        await userEvent.type(
            screen.getByLabelText("Songwriter's IPI"),
            '62018705'
        );
        await userEvent.click(screen.getAllByText('Yes')[2]);

        await userEvent.click(screen.getByText('Select Publisher...'));
        await userEvent.click(screen.getByText('+ Add New Publisher'));

        await userEvent.type(
            screen.getByLabelText('Publishing Company Name'),
            'PubComp'
        );
        await userEvent.click(screen.getByText("Enter Publisher's PRO"));
        await userEvent.click(screen.getByText('BMI'));
        await userEvent.type(
            screen.getByLabelText("Publishing Company's IPI"),
            '62018705'
        );

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

    it('can get errors', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Billy Trodd'
        );
        await userEvent.click(screen.getAllByText('Yes')[0]);
        await userEvent.click(screen.getAllByText('Yes')[1]);

        await userEvent.type(
            screen.getByText("Enter Songwriter's PRO"),
            'ASCAP'
        );
        await userEvent.click(screen.getAllByText('ASCAP')[0]);
        await userEvent.type(
            screen.getByLabelText("Songwriter's IPI"),
            '52018705'
        );
        await userEvent.click(screen.getAllByText('Yes')[2]);

        await userEvent.click(screen.getByText('Select Publisher...'));
        await userEvent.click(screen.getByText('+ Add New Publisher'));

        await userEvent.type(
            screen.getByLabelText('Publishing Company Name'),
            'PubComp'
        );
        await userEvent.click(screen.getByText("Enter Publisher's PRO"));
        await userEvent.click(screen.getByText('BMI'));
        await userEvent.type(
            screen.getByLabelText("Publishing Company's IPI"),
            '52018705'
        );

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

        expect(
            screen.getAllByText('The IPI you entered is invalid')
        ).toHaveLength(2);
        expect(createMock).not.toHaveBeenCalled();
    });

    // eslint-disable-next-line jest/expect-expect
    it('can reset on cancel', async () => {
        renderComponent({});
        await waitForLoaded();
        await userEvent.click(screen.getByText('Cancel'));
    });

    it('can register a songwriter but get no PRO warning', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('Jorja Smith'));

        await userEvent.click(screen.getAllByText('Yes')[0]);
        await userEvent.click(screen.getAllByText('No')[1]);

        const m =
            'This songwriter must be registered with a Performance Rights Organization in order to collect on their behalf.';
        expect(screen.getByText(m)).toBeTruthy();
        await userEvent.click(screen.getByText('Save'));
    });

    it('needs very little info when not controlled', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Billy Trodd'
        );
        await userEvent.click(screen.getAllByText('No')[0]);
        await userEvent.click(screen.getByText('Save'));

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                id: '',
                ipi: '',
                label: {
                    uuid: labelsMock.data[0].uuid,
                },
                name: 'Billy Trodd',
                pro: '',
                professionallyKnownAs: null,
                publishers: [
                    {
                        controlled: false,
                        excludedTerritories: [],
                        ipi: '',
                        name: '',
                        pro: '',
                    },
                ],
            },
        });
    });

    it('cannot save songwriter when LIMIT_MODIFY_ACCESS FF is enabled', async () => {
        renderComponent({
            identity: {
                features: { [FEATURE_FLAGS.LIMIT_MODIFY_ACCESS]: true },
            },
        });
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Billy Trodd'
        );
        await userEvent.click(screen.getAllByText('No')[0]);

        const modalClasses = screen.getByTestId('Modal').classList.values();

        let isClassInList = false;
        for (const value of modalClasses)
            if (value === 'save-disabled') isClassInList = true;

        expect(isClassInList).toBeTruthy();
    });

    it('can have partial info when not controlled', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Billy Trodd'
        );
        await userEvent.click(screen.getAllByText('No')[0]);

        await userEvent.click(screen.getAllByText('Yes')[1]);

        await userEvent.type(
            screen.getByText("Enter Songwriter's PRO"),
            'ASCAP'
        );
        await userEvent.click(screen.getAllByText('ASCAP')[0]);

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

        await waitFor(() => {
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                'Click - Created Songwriter',
                {
                    category: 'New Songwriter',
                    songWriterId: '',
                    songWriterIpi: '',
                    songWriterLegalName: 'Billy Trodd',
                    songWriterPro: 'ASCAP',
                    songWriterProfessionallyKnownAs: null,
                }
            );
        });

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                id: '',
                ipi: '',
                label: {
                    uuid: labelsMock.data[0].uuid,
                },
                name: 'Billy Trodd',
                pro: 'ASCAP',
                professionallyKnownAs: null,
                publishers: [
                    {
                        controlled: false,
                        excludedTerritories: [],
                        ipi: '',
                        name: '',
                        pro: '',
                    },
                ],
            },
        });
    });

    it('can save professionallyKnowsAs', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Test Songwriter'
        );

        await userEvent.click(screen.getByText('Add Professionally Known As'));
        await userEvent.type(
            screen.getAllByPlaceholderText('Enter Professionally Known As')[0],
            'Test Songwriter PKA 1'
        );
        await userEvent.click(screen.getByText('Add Professionally Known As'));
        await userEvent.type(
            screen.getAllByPlaceholderText('Enter Professionally Known As')[1],
            'Test Songwriter PKA 2'
        );

        await userEvent.click(screen.getAllByText('No')[0]);
        await userEvent.click(screen.getByText('Save'));

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                id: '',
                ipi: '',
                label: {
                    uuid: labelsMock.data[0].uuid,
                },
                name: 'Test Songwriter',
                pro: '',
                professionallyKnownAs: [
                    'Test Songwriter PKA 1',
                    'Test Songwriter PKA 2',
                ],
                publishers: [
                    {
                        controlled: false,
                        excludedTerritories: [],
                        ipi: '',
                        name: '',
                        pro: '',
                    },
                ],
            },
        });
    });

    it('should save professionallyKnowsAs as null', async () => {
        renderComponent({});
        await waitForLoaded();

        expect(screen.getByText(labelsMock.data[0].name)).toBeInTheDocument();

        await userEvent.click(screen.getByText('Search for Songwriter'));
        await userEvent.click(screen.getByText('+ Add New Songwriter'));

        await userEvent.type(
            screen.getByLabelText('Songwriter Legal Name'),
            'Test Songwriter'
        );

        await userEvent.click(screen.getAllByText('No')[0]);
        await userEvent.click(screen.getByText('Save'));

        expect(createMock).toHaveBeenCalledWith({
            variables: {
                id: '',
                ipi: '',
                label: {
                    uuid: labelsMock.data[0].uuid,
                },
                name: 'Test Songwriter',
                pro: '',
                professionallyKnownAs: null,
                publishers: [
                    {
                        controlled: false,
                        excludedTerritories: [],
                        ipi: '',
                        name: '',
                        pro: '',
                    },
                ],
            },
        });
    });
});
