import React, { useState } from 'react';
import { render, act, fireEvent, screen, waitFor } from '@testing-library/react';
import { ResourceBrowserInput, ResourceBrowserInputProps } from './ResourceBrowserInput';
import { mockSource, mockResource } from '../__mocks__/MockModels';
import { ResourceBrowserPlugin, ResourceBrowserUnresolvedResource } from '../types';

import * as RP from '../ResourcePicker/ResourcePicker';
jest.spyOn(RP, 'ResourcePicker');

describe('Resource browser input', () => {
    const mockUseResource = jest.fn().mockReturnValue({
        data: null,
        error: null,
        isLoading: false,
        reload: () => {},
    });
    const mockChange = jest.fn();
    const mockSetSource = jest.fn();
    const mockOnModalStateChange = jest.fn();
    const renderComponent = (props: Partial<ResourceBrowserInputProps> = {}) => {
        return render(
            <ResourceBrowserInput
                modalTitle="Asset picker"
                value={null}
                isOtherSourceValue={false}
                useResource={mockUseResource}
                onChange={mockChange}
                plugin={null}
                pluginMode={null}
                searchEnabled={false}
                source={null}
                sources={[]}
                isLoading={false}
                error={null}
                setSource={mockSetSource}
                isModalOpen={false}
                onModalStateChange={mockOnModalStateChange}
                {...props}
            />,
        );
    };

    it('If onClear function is provided passes through to ResourcePicker', async () => {
        const myOnClear = jest.fn();
        renderComponent({ onClear: myOnClear });

        await waitFor(() => {
            expect(RP.ResourcePicker).toHaveBeenCalledWith(
                expect.objectContaining({
                    onClear: myOnClear,
                }),
                {},
            );
        });
    });

    it('If onClear function is not provided creates one which nulls change', async () => {
        renderComponent();

        await waitFor(() => {
            expect(RP.ResourcePicker).toHaveBeenCalled();
        });
        expect(mockChange).not.toHaveBeenCalled();

        // Invoke modal close callback
        const { onClear } = (RP.ResourcePicker as unknown as jest.SpyInstance).mock.calls[0][0];
        onClear();
        expect(mockChange).toHaveBeenCalledWith(null);
    });

    /*
        These tests dont actually belong in this component as they do not control these items directly
        and never have but were tested here when this was originally written.
    */
    describe('Legacy tests', () => {
        it('should render the related asset picker with the default label', async () => {
            renderComponent();

            await waitFor(() => {
                expect(screen.getByText('Choose asset')).toBeInTheDocument();
            });
        });

        it('should display the generic asset picking icon', async () => {
            renderComponent();

            await waitFor(() => {
                expect(screen.getByText('Choose asset')).toBeInTheDocument();
                expect(screen.getByTestId('AdsClickRoundedIcon')).toBeInTheDocument();
            });
        });

        it('should display the image icon when only images are allowed', async () => {
            renderComponent({ allowedTypes: ['image'] });

            await waitFor(() => {
                expect(screen.getByText('Choose image')).toBeInTheDocument();
                expect(screen.getByTestId('PhotoLibraryRoundedIcon')).toBeInTheDocument();
            });
        });

        it('should disallow removing and replacing selection if the input is disabled', async () => {
            const source = mockSource();
            const resource = mockResource({ source: source });
            mockUseResource.mockReturnValueOnce({
                data: resource,
                error: null,
                isLoading: false,
                reload: () => {},
            });
            const plugin: Partial<ResourceBrowserPlugin> = {
                renderSelectedResource: () => {
                    return Promise.resolve({
                        icon: <div>IconGoesHere</div>,
                        label: 'Products',
                        description: [],
                    });
                },
            };

            renderComponent({
                value: { sourceId: source.id, resourceId: '100' },
                isDisabled: true,
                plugin: plugin as ResourceBrowserPlugin,
            });

            await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());

            expect(screen.getByRole('button', { name: 'Remove selection' })).toBeDisabled();
            expect(screen.getByRole('button', { name: 'Replace selection' })).toBeDisabled();
        });

        it('clicking the replace selection button should open the resource browser', async () => {
            const source = mockSource();
            const resource = mockResource({ source: source });
            mockUseResource.mockReturnValueOnce({
                data: resource,
                error: null,
                isLoading: false,
                reload: () => {},
            });
            const plugin: Partial<ResourceBrowserPlugin> = {
                renderSelectedResource: () => {
                    return Promise.resolve({
                        icon: <div>IconGoesHere</div>,
                        label: 'Products',
                        description: [],
                    });
                },
                sourceBrowserComponent: () => {
                    return () => {
                        return <></>;
                    };
                },
                sourceSearchComponent: () => {
                    return () => {
                        return <></>;
                    };
                },
            };

            renderComponent({
                value: { sourceId: source.id, resourceId: '100' },
                plugin: plugin as ResourceBrowserPlugin,
            });

            await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());
            await act(() => fireEvent.click(screen.getByRole('button', { name: 'Replace selection' })));

            // Modal index state change handler was instructed to open
            expect(mockOnModalStateChange).toHaveBeenCalledWith(true);
        });

        it.each([
            ['resource selected', { sourceId: mockSource().id, resourceId: '100' }, 1],
            ['resource not selected', null, 0],
        ])(
            'should only display the "replace" button if a resource has already been selected - %s',
            async (description: string, value: ResourceBrowserUnresolvedResource | null, expectedReplaceSelectionButtons: number) => {
                const source = mockSource();
                const resource = mockResource({ source: source });
                mockUseResource.mockReturnValueOnce({
                    data: value,
                    error: null,
                    isLoading: false,
                    reload: () => {},
                });
                const plugin: Partial<ResourceBrowserPlugin> = {
                    renderSelectedResource: () => {
                        return Promise.resolve({
                            icon: <div>IconGoesHere</div>,
                            label: 'Products',
                            description: [],
                        });
                    },
                };
                renderComponent({ value, plugin: plugin as ResourceBrowserPlugin });

                await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());

                expect(screen.queryAllByRole('button', { name: 'Replace selection' })).toHaveLength(expectedReplaceSelectionButtons);
            },
        );
    });
});
