import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { wait } from 'lib/wait';
import { ArtistStoreProfile } from '../../../../types';
import * as analytics from '../../../../utils/analytics';
import ArtistProfilesPagination, {
    Props,
    OnValidateCallback,
    OnDoneCallback,
} from '../artistProfilesPagination';

interface PageContext {
    id?: string;
    className: string;
    key: number;
    store: number;
    tabId: string;
    data: ArtistStoreProfile[];
    value?: string;
}

interface ValidationData {
    id: string;
    value: string;
}

describe('<ArtistProfilesPagination>', () => {
    const pagesContext = [
        { className: 'text-class-1', key: 1, data: [], store: 1, tabId: '1' },
        { className: 'text-class-2', key: 2, data: [], store: 1, tabId: '2' },
        { className: 'text-class-3', key: 3, data: [], store: 1, tabId: '3' },
    ];
    const getDefaultProps = () => ({
        pagesContext,
        onDone: jest.fn(),
        backText: 'Back',
        nextText: 'Next',
        doneText: 'Done',
        onValidate: jest.fn(),
    });

    const renderComponent = (props: Props<PageContext, ValidationData>) =>
        shallow(
            <ArtistProfilesPagination {...props}>
                {(data, context) => (
                    <div key={data.tabId} {...context}>
                        {data.id?.toString() ?? 'id'}
                    </div>
                )}
            </ArtistProfilesPagination>
        );

    const renderPage = (page: number, props: Props<PageContext, ValidationData>) => {
        const component = renderComponent(props).setState({ page });
        component.setState({ page });
        return component;
    };

    const getInstance = (component: ShallowWrapper) =>
        component.instance() as ArtistProfilesPagination;

    const validateComponent = (
        pageData: PageContext,
        onValidate?: OnValidateCallback<ValidationData>
    ): [ShallowWrapper, OnDoneCallback<ValidationData>] => {
        const onDone = jest.fn();
        const testProps = { ...getDefaultProps(), onValidate, onDone };
        const component = renderComponent(testProps);
        const onPageValidate: (data: PageContext) => void = component
            .find('[isVisible=true]')
            .prop('onValidate');
        onPageValidate(pageData);
        (component.instance() as ArtistProfilesPagination).handleDone();
        return [component, onDone];
    };

    it('renders the expected markup', () => {
        const component = renderComponent(getDefaultProps());
        expect(component).toMatchSnapshot();
    });

    it('renders the expected footer markup for the second page', () => {
        const component = renderPage(1, getDefaultProps());
        expect(component.find('.ArtistProfilesPagination-back')).toMatchSnapshot('Back button');
        expect(component.find('.ArtistProfilesPagination-next')).toMatchSnapshot('Next button');
    });

    it('renders the expected footer markup for the last page', () => {
        const component = renderPage(2, getDefaultProps());
        expect(component.find('.ArtistProfilesPagination-back')).toMatchSnapshot('Back button');
        expect(component.find('.ArtistProfilesPagination-done')).toMatchSnapshot('Done button');
    });

    it('does not render the component if pages context is empty', () => {
        const component = renderComponent({
            ...getDefaultProps(),
            pagesContext: [],
        });
        expect(component.find('.ArtistProfilesPagination').exists()).toBeFalsy();
    });

    it('does not render pagination if showDotPages is false', () => {
        const component = renderComponent({
            ...getDefaultProps(),
            showDotPages: false,
        });
        expect(component.find('.ArtistProfilesPagination-page-icon').exists()).toBeFalsy();
    });

    it('does not render pagination if there is only one page', () => {
        const testPagesContext = pagesContext.slice(0, 1);
        const component = renderComponent({
            ...getDefaultProps(),
            pagesContext: testPagesContext,
        });
        expect(component.find('.ArtistProfilesPagination-page-icon').exists()).toBeFalsy();
    });

    it('calls onDone handler when Done clicked', async () => {
        const testProps = getDefaultProps();
        const component = renderPage(2, testProps);
        await wait();
        const onClick = component.find('.ArtistProfilesPagination-done').prop<Function>('onClick');
        onClick();
        expect(testProps.onDone).toHaveBeenCalled();
    });

    it('does not throw errors is onDone handler is not defined', () => {
        const testProps = { ...getDefaultProps(), onDone: undefined };
        const component = renderPage(2, testProps);
        const onClick = component.find('.ArtistProfilesPagination-done').prop<Function>('onClick');
        let noErrors = true;
        try {
            onClick();
        } catch {
            noErrors = false;
        }
        expect(noErrors).toBeTruthy();
    });

    it('calls onDone with default validation context', () => {
        const pageData = { id: 1 } as unknown as PageContext;
        const [component, onDone] = validateComponent(pageData);
        expect(component.state('isPageValid')).toBe(false);
        expect(onDone).toHaveBeenCalledWith({ 0: pageData });
    });

    it('calls onDone with simple validation context', () => {
        const pageData = { id: 2 } as unknown as PageContext;
        const onValidate = jest.fn(({ id }) => id === 3);
        const [component, onDone] = validateComponent(pageData, onValidate);
        expect(component.state('isPageValid')).toBeFalsy();
        expect(onDone).toHaveBeenCalledWith({ 0: pageData });
    });

    it('calls onDone with custom validation context', () => {
        const pageData = {
            id: 'pageId1',
            value: 'test',
        } as unknown as PageContext;
        const [, onDone] = validateComponent(pageData, ({ id, value }) => [true, { [id]: value }]);
        expect(onDone).toHaveBeenCalledWith({ pageId1: pageData.value });
    });

    it('calls handleBack and sets previous page', () => {
        const component = renderPage(1, getDefaultProps());
        getInstance(component).handleBack();
        expect(component.state('page')).toBe(0);
    });

    it('calls handleNext and sets next page', () => {
        const component = renderPage(1, {
            ...getDefaultProps(),
            isAnalyticsMode: true,
        });
        jest.spyOn(analytics, 'trackSegmentEvent').mockReturnValue(undefined);
        component.setState({ lastSelectedProfileIsNull: true });
        getInstance(component).handleNext();
        expect(analytics.trackSegmentEvent).toHaveBeenCalledTimes(1);
        expect(component.state('page')).toBe(2);
    });

    it('calls handleSetPage and sets normalized custom page', () => {
        const component = renderComponent(getDefaultProps());
        getInstance(component).handleSetPage(10);
        expect(component.state('page')).toBe(2);

        getInstance(component).handleSetPage(-10);
        expect(component.state('page')).toBe(0);
    });

    it('calls handleBack and navigates back when current page is invalid', () => {
        const component = renderPage(1, getDefaultProps());
        component.setState({ isPageValid: false });
        getInstance(component).handleBack();
        expect(component.state('page')).toBe(0);
    });

    it('calls handleNext and does not navigate next if current page is invalid', () => {
        const component = renderPage(1, getDefaultProps());
        component.setState({ isPageValid: false });
        getInstance(component).handleNext();
        expect(component.state('page')).toBe(1);
    });
});
