import React from 'react';
import { waitFor, fireEvent, screen } from '@testing-library/react';
import { createHref, Segment } from '@theorchard/suite-frontend';
import { renderInAppContext } from '@theorchard/suite-testing';
import {
    compositionsData,
    compositionDataLoading,
} from 'lib/test/mocks/compositions';
import { labelsMock, singleLabelMock } from 'lib/test/mocks/labels';
import { songWritersResponse } from 'lib/test/mocks/songWriters';
import { MemoryRouter, Route } from 'react-router-dom';
import { ROUTE_PUBLISHING_SONGS_TABS } from 'src/constants';
import * as deleteComposition from 'src/data/mutations/deleteComposition/deleteComposition';
import * as compositions from 'src/data/queries/compositions/compositions';
import * as orchardLabelById from 'src/data/queries/orchardLabel/orchardLabelById';
import * as orchardLabels from 'src/data/queries/orchardLabels/orchardLabels';
import * as publishingSongWriters from 'src/data/queries/songWritersSearch/songWritersSearch';
import * as features from 'src/utils/features';
import SongListPage from '../songListPage';

describe('<SongListPage>', () => {
    const defaultRouteParams = {
        tab: 'submitted',
        page: '1',
    };

    let songWritersFn: jest.SpyInstance;
    let compositionsFn: jest.SpyInstance;
    const deleteMutationSpy = jest.fn();

    const renderPage = (
        tab = defaultRouteParams.tab,
        page = defaultRouteParams.page
    ) => {
        const routeProps = {
            pathname: `/publishing-songs/${tab}`,
            search: `?page=${page}`,
        };

        return renderInAppContext(
            <MemoryRouter
                initialEntries={[
                    createHref(ROUTE_PUBLISHING_SONGS_TABS, {
                        tab,
                    }),
                ]}
            >
                <Route
                    exact
                    path={ROUTE_PUBLISHING_SONGS_TABS}
                    component={SongListPage}
                />
            </MemoryRouter>,
            routeProps
        );
    };

    beforeEach(() => {
        jest.spyOn(Segment, 'trackEvent');

        compositionsFn = jest
            .spyOn(compositions, 'useCompositions')
            .mockReturnValue({
                error: undefined,
                loading: false,
                data: compositionsData,
            });
        songWritersFn = jest
            .spyOn(publishingSongWriters, 'usePublishingSongWriters')
            .mockReturnValue(songWritersResponse);

        jest.spyOn(orchardLabels, 'useOrchardLabels').mockReturnValue(
            labelsMock
        );
        jest.spyOn(
            orchardLabelById,
            'useOrchardLabelByIdQuery'
        ).mockReturnValue({
            data: singleLabelMock.data[0],
            loading: false,
            error: undefined,
        });

        jest.spyOn(deleteComposition, 'useDeleteComposition').mockReturnValue(
            deleteMutationSpy
        );
    });

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

    test('defaults to the submitted songs tab', () => {
        const { container } = renderPage();

        const activeTab =
            container.querySelector<HTMLAnchorElement>('a.nav-link.active');
        expect(activeTab?.href).toMatch('/publishing-songs/submitted?page=1');
    });

    test('adds the decimals correctly', () => {
        renderPage();

        expect(screen.getByText('51.2% Ownership')).toBeTruthy();
    });

    test('has modified dates', () => {
        renderPage('drafts');

        expect(screen.getByText('Modified Date')).toBeTruthy();
        expect(screen.getAllByText('12/12/2021')).toBeTruthy();
        expect(screen.queryByText('Submitted Date')).toBeFalsy();
    });

    test('has submitted dates', () => {
        renderPage();

        expect(screen.getByText('Submitted Date')).toBeTruthy();
        expect(screen.getAllByText('12/11/2021')).toBeTruthy();
        expect(screen.queryByText('Modified Date')).toBeFalsy();
    });

    test('has zero states in rows', () => {
        renderPage();

        expect(screen.getByText('None added')).toBeTruthy();
        expect(screen.getByText('None')).toBeTruthy();
        expect(screen.getByText('No ownership')).toBeTruthy();
    });

    test('can not delete a song if the feature flag is not enabled', () => {
        const { container } = renderPage();

        expect(
            container.querySelectorAll('.TableActionButton.action-delete')
        ).toHaveLength(0);
    });

    test('can delete an existing song when FF is on', () => {
        jest.spyOn(features, 'useDeleteSongFF').mockReturnValue(true);

        const { container, getByText } = renderPage(
            defaultRouteParams.tab,
            defaultRouteParams.page
        );

        const deleteIcon = container
            .querySelectorAll('.TableActionButton.action-delete')
            .item(0);
        fireEvent.click(deleteIcon);

        const confirmDeleteButton = getByText(
            'Yes, I want to delete this song'
        );
        expect(confirmDeleteButton).toBeTruthy();

        fireEvent.click(confirmDeleteButton);
        expect(Segment.trackEvent).toHaveBeenCalledWith('Click - Delete Song', {
            category: 'Delete Song',
            songId: '1',
            songTitle: 'Be Honest',
        });
        expect(deleteMutationSpy).toHaveBeenCalledWith({
            variables: { id: '1' },
        });
    });

    test('can switch tabs', async () => {
        const { container } = renderPage();

        const draftTab = screen.getByText('drafts');
        await waitFor(() => expect(draftTab).toBeVisible());
        fireEvent.click(draftTab);

        expect(Segment.trackEvent).toHaveBeenCalledWith(
            'Click - Submitted Tab',
            { category: 'Songs' }
        );
        const activeTab =
            container.querySelector<HTMLAnchorElement>('a.nav-link.active');
        expect(activeTab?.href).toMatch('/publishing-songs/drafts?page=1');
    });

    test('saves search text when switching tabs', () => {
        renderPage();
        const text = '50 C';
        const searchInput = screen.getByPlaceholderText(
            'Search...'
        ) as HTMLInputElement;
        fireEvent.change(searchInput, { target: { value: text } });

        const draftTab = screen.getByText('drafts');
        fireEvent.click(draftTab);
        const searchInputDraft = screen.getByPlaceholderText(
            'Search...'
        ) as HTMLInputElement;
        expect(searchInputDraft.value).toBe(text);
    });

    test('can search songs', () => {
        renderPage();

        const searchInput = screen.getByPlaceholderText(
            'Search...'
        ) as HTMLInputElement;
        fireEvent.change(searchInput, { target: { value: '50 C' } });
        expect(searchInput.value).toBe('50 C');
    });

    test('can filter by songwriters', () => {
        const { container } = renderPage();

        const asyncSelect = container.querySelector(
            '.SongWriterSelector input'
        );
        if (!asyncSelect) throw new Error('Could not find .Select.Async input');
        fireEvent.change(asyncSelect, { target: { value: '50 C' } });

        expect(asyncSelect).toBeTruthy();
        expect(songWritersFn).toHaveBeenCalled();
    });

    test('can filter by label', () => {
        const { container } = renderPage();

        const asyncSelect = container.querySelectorAll(
            '.Select__control input'
        )[1];
        fireEvent.change(asyncSelect, { target: { value: 'Cow' } });

        expect(asyncSelect).toBeTruthy();
        expect(songWritersFn).toHaveBeenCalled();
    });

    test('can count Associated Recordings', () => {
        const { container } = renderPage();

        const count = container.querySelector(
            '.GridTable .cell-associatedSoundRecordings'
        );
        expect(count?.textContent).toEqual('2');
    });

    test('can show empty state', () => {
        const data = {
            totalCount: 0,
            compositions: [],
        };

        jest.spyOn(compositions, 'useCompositions').mockReturnValue({
            data,
            loading: false,
            error: undefined,
        });
        renderPage();

        expect(screen.getByText('You Have No Songs')).toBeTruthy();
    });

    test('shows loading state', () => {
        jest.spyOn(compositions, 'useCompositions').mockReturnValue({
            data: compositionDataLoading,
            loading: true,
            error: undefined,
        });

        renderPage();

        expect(screen.getByText('Searching Songs')).toBeTruthy();
    });

    test('common Label Picker should be render at header if ALLOW_LABEL_PICKER FF is on', () => {
        jest.spyOn(features, 'useLabelPickerFF').mockReturnValue(true);

        const { container } = renderPage(
            defaultRouteParams.tab,
            defaultRouteParams.page
        );

        const headerLabelPicker = container.querySelector(
            '.SongListPage .PageHeader .HeaderLabelPicker .LabelSearchDropdown'
        );
        expect(headerLabelPicker).toBeTruthy();

        const pageTitle = container.querySelector(
            '.SongListPage .PageHeader .PageTitle'
        );
        expect(pageTitle).toBeFalsy();

        const listingLabelPickers = container.querySelector(
            '.SongListPage .PageBody .PageToolbar .LabelSearchDropdown'
        );
        expect(listingLabelPickers).toBeFalsy();
    });

    test('Label Picker should be render in the listing filters if ALLOW_LABEL_PICKER FF is off', () => {
        const { container } = renderPage();

        const headerLabelPicker = container.querySelector(
            '.SongListPage .PageHeader .HeaderLabelPicker .LabelSearchDropdown'
        );
        expect(headerLabelPicker).toBeFalsy();

        const pageTitle = container.querySelector(
            '.SongListPage .PageHeader .PageTitle'
        );
        expect(pageTitle).toBeTruthy();

        const listingLabelPickers = container.querySelector(
            '.SongListPage .PageBody .PageToolbar .LabelSearchDropdown'
        );
        expect(listingLabelPickers).toBeTruthy();
    });

    test('user with single label can save filter state', () => {
        jest.spyOn(orchardLabels, 'useOrchardLabels').mockReturnValue(
            singleLabelMock
        );

        const { container } = renderPage(
            defaultRouteParams.tab,
            defaultRouteParams.page
        );

        const searchInput = screen.getByPlaceholderText(
            'Search...'
        ) as HTMLInputElement;
        fireEvent.mouseDown(searchInput);
        fireEvent.change(searchInput, { target: { value: 'Gustav' } });

        // song writer select
        const swSelect = container.querySelectorAll(
            '.SongWriterSelector input'
        )[0];
        fireEvent.mouseDown(swSelect);
        const swOptionSelect = container
            .querySelectorAll('.SongWriterSelector .Select__option')
            .item(0);
        fireEvent.click(swOptionSelect);

        renderPage(defaultRouteParams.tab, defaultRouteParams.page);

        expect(compositionsFn).toHaveBeenCalledWith({
            draft: false,
            label: singleLabelMock.data[0],
            page: 1,
            songWriters: ['Gustav Tomas Skjerdal Wood'],
            titleSearch: 'Gustav',
        });
    });

    test('user with several labels can save filter state', () => {
        const { container } = renderPage();

        const searchInput = screen.getByPlaceholderText(
            'Search...'
        ) as HTMLInputElement;
        fireEvent.change(searchInput, { target: { value: 'Gustav' } });

        // label select
        const labelSelect = container.querySelectorAll(
            '.LabelSearchDropdown input'
        )[0];
        fireEvent.mouseDown(labelSelect);
        const labelOptionSelect = container.querySelectorAll(
            '.LabelSearchDropdown .Select__option'
        )[0];
        fireEvent.click(labelOptionSelect);

        // song writer select
        const swSelect = container.querySelectorAll(
            '.SongWriterSelector input'
        )[0];
        fireEvent.mouseDown(swSelect);
        const swOptionSelect = container.querySelectorAll(
            '.SongWriterSelector .Select__option'
        )[0];
        fireEvent.click(swOptionSelect);

        renderPage();

        expect(compositionsFn).toHaveBeenCalledWith({
            draft: false,
            label: labelsMock.data[0],
            page: 1,
            songWriters: ['Gustav Tomas Skjerdal Wood'],
            titleSearch: 'Gustav',
        });
    });

    test('Label Picker should be rendered in the listing filters if ALLOW_LABEL_PICKER FF is off', () => {
        const { container } = renderPage();

        const headerLabelPicker = container.querySelector(
            '.SongListPage .PageHeader .HeaderLabelPicker .LabelSearchDropdown'
        );
        expect(headerLabelPicker).toBeFalsy();

        const pageTitle = container.querySelector(
            '.SongListPage .PageHeader .PageTitle'
        );
        expect(pageTitle).toBeTruthy();

        const listingLabelPickers = container.querySelector(
            '.SongListPage .PageBody .PageToolbar .LabelSearchDropdown'
        );
        expect(listingLabelPickers).toBeTruthy();
    });

    describe('When ALLOW_LABEL_PICKER FF is on', () => {
        test('common Label Picker should be rendered at header if FF is on', () => {
            jest.spyOn(features, 'useLabelPickerFF').mockReturnValue(true);
            const { container } = renderPage(
                defaultRouteParams.tab,
                defaultRouteParams.page
            );

            const headerLabelPicker = container.querySelector(
                '.SongListPage .PageHeader .HeaderLabelPicker .LabelSearchDropdown'
            );
            expect(headerLabelPicker).toBeTruthy();

            const pageTitle = container.querySelector(
                '.SongListPage .PageHeader .PageTitle'
            );
            expect(pageTitle).toBeFalsy();

            const listingLabelPickers = container.querySelector(
                '.SongListPage .PageBody .PageToolbar .LabelSearchDropdown'
            );
            expect(listingLabelPickers).toBeFalsy();
        });
    });
});
