import React from 'react';
import { fireEvent, screen, within } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import { waitForLoaded } from 'lib/test/helpers';
import * as publisherSearch from 'src/data/queries/publisherSearch/publisherSearch';
import { songWritersQueryData } from 'src/pages/songWriterListPage/mocks';
import { Songwriter } from 'src/types';
import { SECTION_CLASSNAME } from '../components/associatedCompositions/associatedCompositions';
import SongWriterEditModal from '../songWriterEditModal';

const pushMock = jest.fn((route: string) => createMemoryHistory().push(route));
const writerData = songWritersQueryData.songWriters[0] as Songwriter;
const locationPathnameMock = `/song-writer/${writerData.id}}`;

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

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

describe('<SongWriterEditModal>', () => {
    const renderComponent = (contextProps = {}) => {
        const defaultProps = {
            pathname: locationPathnameMock,
            state: { songWriter: writerData },
        };

        const props = {
            ...defaultProps,
            ...contextProps,
        };

        return renderInAppContext(<SongWriterEditModal />, props);
    };

    beforeEach(() => {
        jest.spyOn(publisherSearch, 'useGetPublishersQuery').mockReturnValue({
            loading: false,
            error: undefined,
            data: [
                {
                    id: '10',
                    name: 'Publisher A',
                },
                {
                    id: '11',
                    name: 'Wow Publisher',
                },
            ],
        });
        window.HTMLElement.prototype.scrollTo = jest.fn();
    });

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

    test('has a cancel and save button', async () => {
        renderComponent();
        await waitForLoaded();

        expect(screen.getByText('Cancel')).toBeTruthy();
        expect(screen.getByText('Save')).toBeTruthy();
    });

    test('has some key metadata about the selected writer in the header', async () => {
        renderComponent();
        await waitForLoaded();

        expect(screen.getByText(writerData.legalName)).toBeTruthy();

        const controlledText = screen.getByText((_, element) => {
            const hasText = (node: HTMLElement) =>
                node.textContent === 'Controlled';
            const node = element as HTMLElement;
            return (
                hasText(node) && node.nextSibling?.textContent?.trim() === '1'
            );
        });

        const nonControlledText = screen.getByText((_, element) => {
            const hasText = (node: HTMLElement) =>
                node.textContent === 'Non-Controlled';
            const node = element as HTMLElement;
            return (
                hasText(node) && node.nextSibling?.textContent?.trim() === '0'
            );
        });

        expect(controlledText).toBeTruthy();
        expect(nonControlledText).toBeTruthy();
    });

    test('has associated compositions section', async () => {
        const { container } = renderComponent();
        await waitForLoaded();

        expect(screen.getByText('Associated Compositions')).toBeTruthy();

        expect(
            container.getElementsByClassName(
                `${SECTION_CLASSNAME}-controlledComps`
            )
        ).toBeTruthy();
        expect(
            container.getElementsByClassName(
                `${SECTION_CLASSNAME}-nonControlledComps`
            )
        ).toBeTruthy();
    });

    test('has some metadata in the basics section', async () => {
        renderComponent();
        await waitForLoaded();

        expect(screen.getByText('Basics')).toBeTruthy();
        expect(screen.getByText('Writer Legal Name')).toBeTruthy();
        expect(screen.getByText('Professionally Known As')).toBeTruthy();
        expect(screen.getByText('Add another name')).toBeTruthy();
        expect(screen.getByText('PRO')).toBeTruthy();
        expect(screen.getByText('IPI')).toBeTruthy();
        expect(screen.getByDisplayValue(writerData.pro as string)).toBeTruthy();
        expect(screen.getByDisplayValue(writerData.ipi as string)).toBeTruthy();
        for (const pka of writerData.professionallyKnownAs) {
            expect(screen.getByDisplayValue(pka)).toBeTruthy();
        }
    });

    test('associated compositions section has Publisher dropdown', async () => {
        renderComponent();
        await waitForLoaded();

        expect(
            await screen.findByTestId('publisherSearchDropdown')
        ).toBeTruthy();
    });

    test('renders No Items message when filtering by publisher', async () => {
        renderComponent();
        await waitForLoaded();

        const select = screen.getByTestId('publisherSearchDropdown');
        const input = within(select).getByRole('combobox', {
            hidden: true,
        }) as HTMLInputElement;

        fireEvent.click(input);

        const dropdownOption = await screen.findByText('Publisher A');

        fireEvent.click(dropdownOption);

        const noItemsMessage = await screen.findAllByText(
            'No items in this list for the selected filter.'
        );
        expect(noItemsMessage).toBeTruthy();
    });

    test('renders agreement(s) with matching publisher', async () => {
        renderComponent();
        await waitForLoaded();

        const select = screen.getByTestId('publisherSearchDropdown');
        const input = within(select).getByRole('combobox', {
            hidden: true,
        }) as HTMLInputElement;

        fireEvent.click(input);

        const dropdownOption = await screen.findByText('Wow Publisher');

        fireEvent.click(dropdownOption);

        const controlledComps = await screen.findByText('Test i');
        const noItemsMessage = await screen.findByText(
            'No Non-Controlled Compositions under this Writer'
        );
        expect(controlledComps).toBeTruthy();
        expect(noItemsMessage).toBeTruthy();
    });
});
