import React from 'react';
import * as apollo from '@apollo/client';
import {
    fireEvent,
    Matcher,
    screen,
    SelectorMatcherOptions,
} from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import { mockAudioProductSearchQuery } from 'lib/test/mockProductSearch';
import { Router } from 'react-router-dom';
import {
    ROUTE_CREATE_BUNDLE_AUDIO_VIDEO,
    ROUTE_CREATE_BUNDLE_VIDEO_ONLY,
} from 'src/constants';
import { BundleType } from 'src/data/globalTypes';
import * as createBundledProductMutation from 'src/data/mutations/createBundledProduct';
import CreatePage from '..';

describe('CreatePage', () => {
    beforeEach(() => {
        jest.spyOn(apollo, 'useApolloClient').mockReturnValue({
            query: mockAudioProductSearchQuery,
        } as unknown as apollo.ApolloClient<object>);

        jest.spyOn(
            createBundledProductMutation,
            'useCreateBundledProductMutation'
        ).mockImplementation(
            (onSubmitCallback: (productId: string | null) => void) => () =>
                onSubmitCallback('2958087')
        );
    });

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

    const selectAudioProduct = async (
        getByText: (
            text: Matcher,
            options?: SelectorMatcherOptions | undefined,
            waitForElementOptions?: unknown
        ) => HTMLElement
    ) => {
        const input = screen
            .getByText(
                $t('bundles.createPage.audioProductsDropdownPlaceholder')
            )
            ?.closest('.Select__value-container')
            ?.querySelector('input');

        if (input) fireEvent.change(input, { target: { value: 'test1' } });
        await new Promise(r => setTimeout(r, 1000));
        fireEvent.click(getByText('test1 (123451)'));
    };

    const selectVideoProduct = async (
        getByText: (
            text: Matcher,
            options?: SelectorMatcherOptions | undefined,
            waitForElementOptions?: unknown
        ) => HTMLElement,
        value: string
    ) => {
        const videoInput = screen
            .getByText(
                $t('bundles.createPage.videoProductsDropdownPlaceholder')
            )
            ?.closest('.Select__value-container')
            ?.querySelector('input');

        if (videoInput) fireEvent.change(videoInput, { target: { value } });
        await new Promise(r => setTimeout(r, 1000));
        fireEvent.click(getByText(value));
    };

    const selectBundleType = async (
        getByText: (
            text: Matcher,
            options?: SelectorMatcherOptions | undefined,
            waitForElementOptions?: unknown
        ) => HTMLElement,
        bundleType: BundleType
    ) => {
        const input = screen
            .getByText($t('bundles.selectPage.selectBundleDropdownPlaceholder'))
            ?.closest('.Select__value-container')
            ?.querySelector('input');
        const value = $t(
            `bundles.selectPage.selectBundleDropdownOption.${bundleType}`
        );
        if (input) fireEvent.change(input, { target: { value } });
        await new Promise(r => setTimeout(r, 1000));
        fireEvent.click(getByText(value));
    };

    test('correct text renders for audio and video', async () => {
        renderInAppContext(<CreatePage bundleType={BundleType.AUDIO_VIDEO} />);
        expect(
            await screen.findByText($t('bundles.createPage.createBundleTitle'))
        ).toBeInTheDocument();
        expect(
            await screen.findByText(
                $t('bundles.createPage.createBundleSubtitle')
            )
        ).toBeInTheDocument();
    });

    test('correct text renders for video only', async () => {
        renderInAppContext(<CreatePage bundleType={BundleType.VIDEO} />);
        expect(
            await screen.findByText($t('bundles.createPage.createBundleTitle'))
        ).toBeInTheDocument();
        expect(
            await screen.findByText(
                $t('bundles.createPage.createBundleSubtitle')
            )
        ).toBeInTheDocument();
    });

    test('audio product selection component renders', async () => {
        renderInAppContext(<CreatePage bundleType={BundleType.AUDIO_VIDEO} />);
        expect(
            await screen.findByText(
                $t('bundles.createPage.audioProductsDropdownTitle')
            )
        ).toBeInTheDocument();
        expect(
            await screen.findByText(
                $t('bundles.createPage.audioProductsDropdownPlaceholder')
            )
        ).toBeInTheDocument();
    });

    test('video product selection component renders', async () => {
        renderInAppContext(<CreatePage bundleType={BundleType.AUDIO_VIDEO} />);
        expect(
            await screen.findByText(
                $t('bundles.createPage.videoProductsDropdownTitle')
            )
        ).toBeInTheDocument();
        expect(
            await screen.findByText(
                $t('bundles.createPage.videoProductsDropdownPlaceholder')
            )
        ).toBeInTheDocument();
    });

    test('video product selection component is disabled', () => {
        renderInAppContext(<CreatePage bundleType={BundleType.AUDIO_VIDEO} />);
        expect(
            screen
                .getByText(
                    $t('bundles.createPage.videoProductsDropdownPlaceholder')
                )
                ?.closest('.Select__value-container')
                ?.querySelector('input')
        ).toBeDisabled();
    });

    test('video product selection component is not disabled when audio is selected', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        expect(
            screen
                .getByText(
                    $t('bundles.createPage.videoProductsDropdownPlaceholder')
                )
                ?.closest('.Select__value-container')
                ?.querySelector('input')
        ).not.toBeDisabled();
    });

    test('alert appears for video only when video product is selected', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        expect(
            screen.getByText($t('bundles.createPage.videoBundleAlert'))
        ).toBeInTheDocument();
    });

    test('alert does not appear for audio/video when video product is selected', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        expect(
            screen.queryByText($t('bundles.createPage.videoBundleAlert'))
        ).not.toBeInTheDocument();
    });

    test('video product shows in list on selection', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        expect(
            // eslint-disable-next-line testing-library/prefer-presence-queries
            screen.queryByText(
                $t('bundles.sortableList.videoProductListLabel', {
                    numItems: 1,
                })
            )
        ).toBeInTheDocument();
        // eslint-disable-next-line testing-library/prefer-presence-queries
        expect(screen.queryByText('volume 1')).toBeInTheDocument();
        expect(screen.getByText('test2 (123452)')).toBeInTheDocument();
    });

    test('volume gets added on button click', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        fireEvent.click(
            screen.getByText($t('bundles.sortableList.volumeButtonLabel'))
        );
        expect(screen.getByText('volume 2')).toBeInTheDocument();
    });

    test('video product gets deleted', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        const close = getByText('test2 (123452)')
            ?.closest('.sortable-item')
            ?.querySelector('.sortable-item-close');
        if (close) {
            fireEvent.click(close);
        }
        expect(
            screen.queryByText(
                $t('bundles.sortableList.videoProductListLabel', {
                    numItems: 1,
                })
            )
        ).not.toBeInTheDocument();
        expect(screen.queryByText('test2 (123452)')).not.toBeInTheDocument();
    });

    test('volume gets deleted', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        fireEvent.click(
            screen.getByText($t('bundles.sortableList.volumeButtonLabel'))
        );
        await selectVideoProduct(getByText, 'test3 (123453)');
        const close = getByText('volume 2')
            ?.closest('.volume-label')
            ?.querySelector('.sortable-item-close');
        if (close) {
            fireEvent.click(close);
            fireEvent.click(
                screen.getByText($t('bundles.volumeContainer.confirm'))
            );
        }
        expect(screen.queryByText('volume 2')).not.toBeInTheDocument();
        expect(screen.queryByText('test3 (123453)')).not.toBeInTheDocument();
    });

    test('success page loads', async () => {
        const { getByText } = renderInAppContext(
            <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');

        const thumbnailSelect = getByText('test2 (123452)')
            ?.closest('.sortable-item-label')
            ?.querySelector('.form-check-input');
        if (thumbnailSelect) {
            fireEvent.click(thumbnailSelect);
        }

        fireEvent.click(
            screen.getByText($t('bundles.createPage.submitButtonLabel'))
        );
        expect(
            screen.getByText($t('bundles.createPage.createBundleSuccessMsg'))
        ).toBeInTheDocument();
    });

    test('create another audio video bundle redirects to correct route', async () => {
        const history = createMemoryHistory();
        const { getByText } = renderInAppContext(
            <Router history={history}>
                <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
            </Router>
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        const thumbnailSelect = getByText('test2 (123452)')
            ?.closest('.sortable-item-label')
            ?.querySelector('.form-check-input');
        if (thumbnailSelect) {
            fireEvent.click(thumbnailSelect);
        }
        fireEvent.click(
            screen.getByText($t('bundles.createPage.submitButtonLabel'))
        );
        await selectBundleType(getByText, BundleType.AUDIO_VIDEO);
        fireEvent.click(
            screen.getByText($t('bundles.createPage.nextButtonLabel'))
        );
        expect(history.location.pathname).toBe(ROUTE_CREATE_BUNDLE_AUDIO_VIDEO);
    });

    test('create another video only bundle redirects to correct route', async () => {
        const history = createMemoryHistory();
        const { getByText } = renderInAppContext(
            <Router history={history}>
                <CreatePage bundleType={BundleType.AUDIO_VIDEO} />
            </Router>
        );
        await selectAudioProduct(getByText);
        await selectVideoProduct(getByText, 'test2 (123452)');
        const thumbnailSelect = getByText('test2 (123452)')
            ?.closest('.sortable-item-label')
            ?.querySelector('.form-check-input');
        if (thumbnailSelect) {
            fireEvent.click(thumbnailSelect);
        }
        fireEvent.click(
            screen.getByText($t('bundles.createPage.submitButtonLabel'))
        );
        await selectBundleType(getByText, BundleType.VIDEO);

        fireEvent.click(
            screen.getByText($t('bundles.createPage.nextButtonLabel'))
        );
        expect(history.location.pathname).toBe(ROUTE_CREATE_BUNDLE_VIDEO_ONLY);
    });
});
