import React from 'react';
import { fireEvent, waitFor, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { renderInAppContext } from '@theorchard/suite-testing';
import { artistsMock } from 'lib/test/mocks/artists';
import { productAndTrackCountMock } from 'lib/test/mocks/productAndTrackCount';
import { vendorAndSubaccountsMock } from 'lib/test/mocks/vendorAndSubaccounts';
import {
    getInputDisplayValue,
    openSelect,
    selectOption,
    clearSelectedValue,
    searchSelectOptions,
    getOption,
    queryOption,
} from 'lib/test-utils/select';
import * as mergeArtistMutation from 'src/data/mutations/mergeArtist/mergeArtist';
import * as artistsQuery from 'src/data/queries/artistSearch/artistSearch';
import * as productAndTrackCountQuery from 'src/data/queries/productAndTrackCount/productAndTrackCount';
import * as vendorAndSubaccountsQuery from 'src/data/queries/vendorAndSubaccounts/vendorAndSubaccounts';
import * as features from 'src/utils/features';
import ArtistNameUpdateSidecar from '../artistNameUpdateSidecar';
import type { ArtistNameUpdateSidecarProps as Props } from 'src/types';

const defaultProps = {
    isOpen: true,
    closeModal: jest.fn(),
};

const querySearchData = [
    {
        id: 'artist-1-uuid',
        name: 'artist-name A',
        artistInfo: [
            {
                id: '1234',
            },
        ],
    },
    {
        id: 'artist-2-uuid',
        name: 'artist-name B',
        artistInfo: [
            {
                id: '4321',
            },
        ],
    },
    {
        id: 'artist-3-uuid',
        name: 'artist-name C',
        artistInfo: [
            {
                id: '6789',
            },
        ],
    },
];

const productAndTrackCount = {
    release: 0,
    track: 2,
};

describe('ArtistNameUpdateSidecar', () => {
    beforeEach(() => {
        jest.spyOn(
            vendorAndSubaccountsQuery,
            'useVendorAndSubaccountsQuery'
        ).mockReturnValue(vendorAndSubaccountsMock);

        jest.spyOn(artistsQuery, 'useArtistSearchQuery').mockReturnValue([
            jest.fn().mockResolvedValue(querySearchData),
            artistsMock,
        ]);

        jest.spyOn(
            productAndTrackCountQuery,
            'useProductAndTrackCountLazyQuery'
        ).mockReturnValue([
            jest.fn().mockResolvedValue(productAndTrackCount),
            productAndTrackCountMock,
        ]);

        HTMLElement.prototype.scrollTo = jest.fn();
    });

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

    const renderComponent = (props: Partial<Props> = {}) =>
        renderInAppContext(
            <ArtistNameUpdateSidecar {...defaultProps} {...props} />
        );

    test('render sidecar and form', () => {
        const { getByTestId } = renderComponent();
        expect(getByTestId('Modal')).toBeInTheDocument();
    });

    test('cancel & close', async () => {
        const { getByRole } = renderComponent();
        const cancelButton = getByRole('button', { name: 'Cancel' });
        fireEvent.click(cancelButton);
        await waitFor(() => {
            expect(defaultProps.closeModal).toHaveBeenCalled();
        });
    });

    test('can see rename option in request type dropdown and see rename specific fields', async () => {
        const { getByText, getByTestId, queryByTestId } = renderComponent();

        expect(queryByTestId('artist-change-name')).not.toBeInTheDocument();
        expect(
            queryByTestId('account-search-dropdown')
        ).not.toBeInTheDocument();
        expect(queryByTestId('artist-search-dropdown')).not.toBeInTheDocument();

        await openSelect('request-type-id');
        selectOption('Rename');

        expect(getByText('Rename')).toBeVisible();

        expect(getByTestId('artist-change-name')).toBeInTheDocument();
        expect(getByTestId('account-search-dropdown')).toBeInTheDocument();
        expect(getByTestId('artist-search-dropdown')).toBeInTheDocument();
    });

    test('can see account search dropdown', async () => {
        const { getByText, getByTestId } = renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        expect(getByTestId('account-label')).toHaveTextContent('Account');
        expect(getByTestId('account-search-dropdown')).toBeInTheDocument();
        expect(getByText('Search for Account by name or ID')).toBeVisible();
        expect(
            getByText('Enter the Account the Artists are associated with')
        ).toHaveClass(
            'ArtistNameUpdateSidecar-account-search-dropdown-helptext'
        );
    });

    test('can select account from accounts dropdown', async () => {
        const { queryByText } = renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        await openSelect('account-search-dropdown');
        selectOption('Account B • 4');
        // when second account option is selected
        expect(getInputDisplayValue('account-search-dropdown')).toBe(
            'Account B • 4'
        );
        //  when selected account option is cleared
        clearSelectedValue('account-search-dropdown');
        expect(queryByText('Search for Account by name or ID')).toBeVisible();

        await openSelect('account-search-dropdown');
        selectOption('Account A • 1');
        // when first account option is selected
        expect(getInputDisplayValue('account-search-dropdown')).toBe(
            'Account A • 1'
        );
    });

    test('clears selected account from accounts dropdown on request type change', async () => {
        const scrollToMock = jest.fn();
        jest.spyOn(HTMLElement.prototype, 'scrollTo').mockImplementation(
            scrollToMock
        );
        jest.spyOn(features, 'useArtistMergeFF').mockReturnValue(true);

        renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        await openSelect('account-search-dropdown');
        selectOption('Account B • 4');
        // when second account option is selected
        expect(getInputDisplayValue('account-search-dropdown')).toBe(
            'Account B • 4'
        );
        // change request type to merge
        await openSelect('request-type-id');
        selectOption('Merge');
        // assert placeholder text
        expect(screen.getByTestId('account-search-dropdown')).toHaveTextContent(
            'Search for Account by name or ID'
        );
    });

    test('can see artist search dropdown', async () => {
        const { getByText, getByTestId } = renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        expect(getByTestId('artist-label')).toHaveTextContent('Artist Name');
        expect(getByTestId('artist-search-dropdown')).toBeInTheDocument();
        expect(getByText('Search for Artist by name or ID')).toBeVisible();
        expect(
            getByText('Enter the Artist Name that needs to be updated')
        ).toHaveClass(
            'ArtistNameUpdateSidecar-artist-search-dropdown-helptext'
        );
    });

    test('can see change name to field for request type "Rename"', async () => {
        const { getByText, getByTestId, getByPlaceholderText } =
            renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        expect(getByTestId('artist-change-name')).toHaveTextContent(
            'Change Name To'
        );
        expect(
            getByPlaceholderText('Enter the updated Artist Name')
        ).toBeDisabled();
        expect(
            getByText('What should the Artist Name be changed to?')
        ).toHaveClass('ArtistNameUpdateSidecar-change-name-to-helptext');
    });

    test('can see Saving.. button for request type "Rename" when clicked on Save', async () => {
        const { getByText, getByTestId, getByPlaceholderText, getByRole } =
            renderComponent();

        await openSelect('request-type-id');
        selectOption('Rename');

        // select account
        await openSelect('account-search-dropdown');
        selectOption('Account A • 1');

        await openSelect('artist-search-dropdown');
        await searchSelectOptions(
            'artist',
            'artist-name A • 1234',
            'artist-search-dropdown'
        );
        // change artist name
        await waitFor(
            async () => {
                await selectOption('artist-name B • 4321');
            },
            { timeout: 1000 }
        );
        // Correct artist has been selected
        expect(getInputDisplayValue('artist-search-dropdown')).toBe(
            'artist-name B • 4321'
        );
        expect(getByTestId('artist-change-name')).toHaveTextContent(
            'Change Name To'
        );
        const inputElement = getByPlaceholderText(
            'Enter the updated Artist Name'
        );
        expect(
            getByText('What should the Artist Name be changed to?')
        ).toHaveClass('ArtistNameUpdateSidecar-change-name-to-helptext');
        expect(getByRole('button', { name: /save/i })).toBeDisabled();

        fireEvent.change(inputElement, { target: { value: 'Test Artist' } });
        fireEvent.click(getByRole('button', { name: /save/i }));

        await waitFor(() => {
            expect(
                getByRole('button', { name: /saving/i })
            ).toBeInTheDocument();
        });
    });

    test('can see merge option in request type dropdown and see merge specific fields', async () => {
        jest.spyOn(features, 'useArtistMergeFF').mockReturnValue(true);

        const { getByText, getByTestId, queryByTestId } = renderComponent();

        expect(
            queryByTestId('account-search-dropdown')
        ).not.toBeInTheDocument();

        await openSelect('request-type-id');
        selectOption('Merge');

        expect(getByText('Merge')).toBeVisible();

        expect(getByTestId('account-search-dropdown')).toBeInTheDocument();
        expect(
            getByTestId('merge-artist-search-dropdown-1').querySelector(
                '.SuiteSelect-input.disabled'
            )
        ).toBeInTheDocument();
        expect(
            getByTestId('merge-artist-search-dropdown-2').querySelector(
                '.SuiteSelect-input.disabled'
            )
        ).toBeInTheDocument();
        expect(
            getByTestId('merge-artist-dropdown').querySelector(
                '.SuiteSelect-input.disabled'
            )
        ).toBeInTheDocument();
    });

    test('can see alert message when artist value is selected', async () => {
        const { getByTestId, queryByText } = renderComponent();
        let bannerElement;

        await openSelect('request-type-id');
        selectOption('Rename');
        // initial state
        expect(queryByText('Search for Account by name or ID')).toBeVisible();
        // select Account option by name or ID
        await openSelect('account-search-dropdown');
        selectOption('Account A • 1');
        // Select artist name dropdown
        await openSelect('artist-search-dropdown');
        await searchSelectOptions(
            'artist',
            'artist-name A • 1234',
            'artist-search-dropdown'
        );
        // change artist name
        await waitFor(
            async () => {
                await selectOption('artist-name B • 4321');
                bannerElement = getByTestId('ArtistNameUpdateSidecar-banner');
                expect(bannerElement).toBeInTheDocument();
            },
            { timeout: 1000 }
        );
        // Correct artist has been selected
        expect(getInputDisplayValue('artist-search-dropdown')).toBe(
            'artist-name B • 4321'
        );
        // display banner text with Alery message
        expect(bannerElement).toHaveTextContent(
            'This artist appears on 0 products & 2 tracks'
        );
    });

    test('can search and select artist in both artist dropdowns for request type merge', async () => {
        jest.spyOn(features, 'useArtistMergeFF').mockReturnValue(true);

        renderComponent();
        // select request type as merge
        await openSelect('request-type-id');
        selectOption('Merge');
        // select account
        await openSelect('account-search-dropdown');
        selectOption('Account A • 1');
        // search Artist Name 1
        await openSelect('merge-artist-search-dropdown-1');
        await searchSelectOptions(
            'artist',
            'artist-name A • 1234',
            'merge-artist-search-dropdown-1'
        );
        // check that all options are available
        expect(getOption('artist-name A • 1234')).toBeInTheDocument();
        expect(getOption('artist-name B • 4321')).toBeInTheDocument();
        expect(getOption('artist-name C • 6789')).toBeInTheDocument();
        // select Artist Name 1
        selectOption('artist-name A • 1234');
        expect(getInputDisplayValue('merge-artist-search-dropdown-1')).toBe(
            'artist-name A • 1234'
        );
        // search Artist Name 2
        await openSelect('merge-artist-search-dropdown-2');
        await searchSelectOptions(
            'artist',
            'artist-name B • 4321',
            'merge-artist-search-dropdown-2'
        );
        // check that expect option selected in Artist name 1 dropdown,
        // other options are available
        expect(queryOption('artist-name A • 1234')).toBeNull();
        expect(getOption('artist-name B • 4321')).toBeInTheDocument();
        expect(getOption('artist-name C • 6789')).toBeInTheDocument();
        // select Artist Name 2
        selectOption('artist-name B • 4321');
        expect(getInputDisplayValue('merge-artist-search-dropdown-2')).toBe(
            'artist-name B • 4321'
        );
        // select Artist Name 1
        await openSelect('merge-artist-search-dropdown-1');
        await searchSelectOptions(
            'artist',
            'artist-name C • 6789',
            'merge-artist-search-dropdown-1'
        );
        // check that expect option selected in Artist name 2 dropdown,
        // other options are available
        expect(getOption('artist-name A • 1234')).toBeInTheDocument();
        expect(queryOption('artist-name B • 4321')).toBeNull();
        expect(getOption('artist-name C • 6789')).toBeInTheDocument();
    });

    test('can select merged artist and save for request type merge', async () => {
        const mergedArtistData = true;
        const mergeLabelParticipantAndArtistMutation = jest
            .fn()
            .mockResolvedValue(mergedArtistData);
        jest.spyOn(features, 'useArtistMergeFF').mockReturnValue(true);
        jest.spyOn(
            mergeArtistMutation,
            'useMergeArtistMutation'
        ).mockReturnValue({
            mergeLabelParticipantAndArtist:
                mergeLabelParticipantAndArtistMutation,
            data: mergedArtistData,
            loading: false,
            error: undefined,
        });

        const { getByRole, getByTestId } = renderComponent();
        // select request type as merge
        await openSelect('request-type-id');
        selectOption('Merge');
        // select account
        await openSelect('account-search-dropdown');
        selectOption('Account A • 1');
        // search and select Artist Name 1
        await openSelect('merge-artist-search-dropdown-1');
        await searchSelectOptions(
            'artist',
            'artist-name A • 1234',
            'merge-artist-search-dropdown-1'
        );
        selectOption('artist-name A • 1234');
        // search and select Artist Name 2
        await openSelect('merge-artist-search-dropdown-2');
        await searchSelectOptions(
            'artist',
            'artist-name B • 4321',
            'merge-artist-search-dropdown-2'
        );
        selectOption('artist-name B • 4321');
        // save button is disabled
        expect(getByRole('button', { name: /save/i })).toBeDisabled();
        // check that the selected Artist Name 1 and 2 options appears in merged artist dropdown
        await openSelect('merge-artist-dropdown');
        expect(getOption('artist-name A • 1234')).toBeInTheDocument();
        expect(getOption('artist-name B • 4321')).toBeInTheDocument();
        // select merge artist
        selectOption('artist-name A • 1234');
        expect(getInputDisplayValue('merge-artist-dropdown')).toBe(
            'artist-name A • 1234'
        );
        // click enabled save button
        fireEvent.click(getByRole('button', { name: /save/i }));
        await waitFor(() => {
            expect(defaultProps.closeModal).toHaveBeenCalled();
        });
        expect(getByTestId('Tooltip')).toBeInTheDocument();
        expect(mergeLabelParticipantAndArtistMutation).toHaveBeenCalledTimes(1);
        expect(mergeLabelParticipantAndArtistMutation).toHaveBeenCalledWith({
            variables: {
                mergedArtistUuid: 'artist-2-uuid',
                uuid1: 'artist-1-uuid',
                uuid2: 'artist-2-uuid',
            },
        });
    });
});
