import React from 'react';
import { waitFor, act } from '@testing-library/react';
import { ResourceBrowser, ResourceBrowserProps } from './index';
import { mockSource, mockResource } from './__mocks__/MockModels';
import { renderWithContext } from './__mocks__/renderWithContext';
import { ResourceBrowserPlugin, ResourceBrowserSource, ResourceBrowserSourceWithPlugin, PluginLaunchModeType } from './types';

import * as RBI from './ResourceBrowserInput/ResourceBrowserInput';
jest.spyOn(RBI, 'ResourceBrowserInput');

import * as Plugin from './Plugin/Plugin';

import * as useSources from './Hooks/useSources';
jest.spyOn(useSources, 'useSources');

var useSourceReloadMock = jest.fn();
jest.mock('./Hooks/useSources', () => {
    const actual = jest.requireActual('./Hooks/useSources');
    return {
        useSources: jest.fn((...args) => {
            const actualResult = actual.useSources(...args);
            return {
                ...actualResult,
                reload: useSourceReloadMock,
            };
        }),
    };
});

describe('Resource browser input', () => {
    const mockChange = jest.fn();
    const mockOnClear = jest.fn();

    const mockRequestSources = jest.fn().mockResolvedValue([]);
    const mockRenderSelectedResource = jest.fn();
    const mockSourceBrowserComponent = jest.fn().mockReturnValue(() => <div>Source UI has been rendered</div>);
    const mockResolveResource = jest.fn();
    const mockUseResolveResource = jest.fn().mockReturnValue({
        data: null,
        error: null,
        isLoading: false,
    });
    const mockDamPlugin = {
        type: 'dam',
        resolveResource: mockResolveResource,
        renderSelectedResource: mockRenderSelectedResource,
        sourceBrowserComponent: mockSourceBrowserComponent,
        useResolveResource: mockUseResolveResource,
        sourceSearchComponent: jest.fn(),
        renderResourceLauncher: jest.fn(),
    } as unknown as ResourceBrowserPlugin;

    const mockMatrixPlugin = {
        type: 'matrix',
        resolveResource: mockResolveResource,
        renderSelectedResource: mockRenderSelectedResource,
        sourceBrowserComponent: mockSourceBrowserComponent,
        useResolveResource: mockUseResolveResource,
        sourceSearchComponent: jest.fn(),
        renderResourceLauncher: jest.fn(),
    } as ResourceBrowserPlugin;

    const plugins = [mockMatrixPlugin, mockDamPlugin];

    const renderComponent = (props: Partial<ResourceBrowserProps> = {}, searchEnabled?: boolean) => {
        return renderWithContext(
            <ResourceBrowser modalTitle="Asset picker" value={null} onChange={mockChange} onClear={mockOnClear} {...props} />,
            {
                onRequestSources: mockRequestSources,
                plugins,
                searchEnabled: !!searchEnabled,
            },
        );
    };

    // Internally the plugin is attached to the source so handle that for tests expectation
    const calculateExpectedSource = (source: ResourceBrowserSource): ResourceBrowserSourceWithPlugin => {
        return {
            ...source,
            plugin: mockDamPlugin,
        };
    };

    it('allowedPlugins will restrict which plugins are used', async () => {
        // Works as expected with no restriction
        (useSources.useSources as jest.Mock).mockClear();
        renderComponent();
        let expectedPlugins = plugins;
        await waitFor(() => {
            expect(useSources.useSources).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    plugins: expectedPlugins,
                }),
            );
        });

        // Works as expected with restrictions
        (useSources.useSources as jest.Mock).mockClear();
        renderComponent({ allowedPlugins: ['matrix'] });
        expectedPlugins = plugins.filter((plugin) => plugin.type === 'matrix');
        await waitFor(() => {
            expect(useSources.useSources).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    plugins: expectedPlugins,
                }),
            );
        });
    });

    it('If only one valid source is provided will default to its Source and Plugin', async () => {
        const source = mockSource({ type: 'dam' });
        mockRequestSources.mockResolvedValueOnce([source]);
        renderComponent();

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculateExpectedSource(source),
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });
    });

    it('If a resource is provided will default to its Source and Plugin to match', async () => {
        const source = mockSource({ type: 'dam' });
        mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
        mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
        renderComponent({ value: { sourceId: source.id, resourceId: '100' } });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculateExpectedSource(source),
                    plugin: mockDamPlugin,
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('If an aliased resource is provided will default to its true Source and Plugin to match', async () => {
        const source = mockSource({ type: 'dam', aliases: ['alias-source-id'] });
        mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
        mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
        renderComponent({ value: { sourceId: 'alias-source-id', resourceId: '100' } });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculateExpectedSource(source),
                    plugin: mockDamPlugin,
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('If a resource is provided but its Source cannot be found it will error', async () => {
        const source = mockSource({ type: 'dam' });
        mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
        mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
        renderComponent({ value: { sourceId: 'not a real id', resourceId: '100' } });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    error: expect.any(Error),
                    source: null,
                    plugin: null,
                }),
                {},
            );
        });
    });

    it('onSourceSelect will alter source passed to ResourceBrowserInput', async () => {
        const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);
        renderComponent();

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                }),
                {},
            );
        });

        // Get the provided callback
        const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke it
        act(() => {
            setSource(calculatedSources[0]);
        });

        // Expect the source and plugin to be loaded
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('onSourceSelect will set mode if provided', async () => {
        const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);
        renderComponent();

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                }),
                {},
            );
        });

        // Get the provided callback
        const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke it
        act(() => {
            setSource(calculatedSources[0], { type: PluginLaunchModeType.Search, args: { query: 'myQuery' } });
        });

        // Expect the source and plugin to be loaded
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                    pluginMode: { type: PluginLaunchModeType.Search, args: { query: 'myQuery' } },
                }),
                {},
            );
        });
    });

    it("When source is selected it will pass down its plugin's useResolveResource to maintain hook rules", async () => {
        const source = mockSource({ type: 'dam' });
        const resource = { resourceId: '12', sourceId: source.id };
        mockRequestSources.mockResolvedValueOnce([source]);
        renderComponent({ value: resource });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    value: resource,
                    source: calculateExpectedSource(source),
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        await waitFor(() => {
            expect(mockUseResolveResource).toHaveBeenCalledWith(resource, calculateExpectedSource(source));
        });
    });

    it('Value will only be passed to ResourceBrowserInput if value source matches currently selected source', async () => {
        const sourcesInput = [mockSource({ id: '1' }), mockSource({ id: '2' })];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        const value = { sourceId: sourcesInput[0].id, resourceId: '123456' };

        renderComponent({ value });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    value,
                    isOtherSourceValue: false,
                    source: calculatedSources[0],
                }),
                {},
            );
        });

        const { onModalStateChange, setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke open the modal
        act(() => {
            onModalStateChange(true);
        });

        // Change the source
        act(() => {
            setSource(calculatedSources[1]);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    value: null,
                    isOtherSourceValue: true,
                    source: calculatedSources[1],
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will reset the Source and Plugin', async () => {
        const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);
        renderComponent();

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                }),
                {},
            );
        });

        // Get the provided callback
        const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke it
        act(() => {
            setSource(calculatedSources[0]);
        });

        // Expect the source and plugin to be loaded
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        // Invoke modal close callback
        const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke open and close the modal
        act(() => {
            onModalStateChange(true);
        });
        act(() => {
            onModalStateChange(false);
        });

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will not reset the Source and Plugin if only one source exists', async () => {
        const sources = [mockSource({ type: 'dam' })];
        mockRequestSources.mockResolvedValueOnce(sources);
        renderComponent();

        // Expect the source and plugin to be loaded
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculateExpectedSource(sources[0]),
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        // Invoke modal close callback
        const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        act(() => {
            onModalStateChange(true);
        });
        act(() => {
            onModalStateChange(false);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculateExpectedSource(sources[0]),
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will reset Source and Plugin if only one source exists AND search is enabled', async () => {
        const sourcesInput = [mockSource({ type: 'dam' })];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);
        renderComponent({}, true);

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                }),
                {},
            );
        });

        // Get the provided callback
        const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke it
        act(() => {
            setSource(calculatedSources[0]);
        });

        // Expect the source and plugin to be loaded
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        // Invoke modal close callback
        const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke open and close the modal
        act(() => {
            onModalStateChange(true);
        });
        act(() => {
            onModalStateChange(false);
        });

        // Expect no source or plugin
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: null,
                    plugin: null,
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will reset the Source and Plugin to the value defaults if a value exists', async () => {
        const sourcesInput = [mockSource({ id: '1' }), mockSource({ id: '2' })];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        renderComponent({ value: { sourceId: sourcesInput[0].id, resourceId: '123456' } });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    sources: calculatedSources,
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        const { onModalStateChange, setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke open and close the modal
        act(() => {
            onModalStateChange(true);
        });

        // Change the source
        act(() => {
            setSource(calculatedSources[1]);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[1],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        act(() => {
            onModalStateChange(false);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will reset the Source and Plugin to the aliases true source if the value uses an alias', async () => {
        const originalSource = mockSource({
            id: 'original-source-id',
            name: 'Original Source',
            type: 'dam',
            aliases: ['alias-source-id'],
        });
        const aliasSourceId = 'alias-source-id';
        const valueWithAlias = {
            sourceId: aliasSourceId,
            resourceId: '123456',
        };

        const sourcesInput = [originalSource, mockSource({ id: '2' })];
        const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        renderComponent({ value: valueWithAlias });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    value: valueWithAlias,
                    sources: calculatedSources,
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        const { onModalStateChange, setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        // Invoke open and close the modal
        act(() => {
            onModalStateChange(true);
        });

        // Change the source
        act(() => {
            setSource(calculatedSources[1]);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    source: calculatedSources[1],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });

        act(() => {
            onModalStateChange(false);
        });

        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    source: calculatedSources[0],
                    plugin: mockDamPlugin,
                }),
                {},
            );
        });
    });

    it('onModalStateChange called with false will reset the Mode', async () => {
        const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        renderComponent({ value: { sourceId: sourcesInput[0].id, resourceId: '123456' } });

        // Select a component and plugin mode
        (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0].setSource(sourcesInput[0], { type: 'browse' });
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                expect.objectContaining({
                    pluginMode: {
                        type: 'browse',
                    },
                }),
                {},
            );
        });

        // Invoke modal close callback
        const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        act(() => {
            onModalStateChange(true);
        });
        act(() => {
            onModalStateChange(false);
        });

        // Expect the mode to be reset
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    pluginMode: null,
                }),
                {},
            );
        });
    });

    it('onRetry reloads sources', async () => {
        const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        renderComponent({ value: { sourceId: sourcesInput[0].id, resourceId: '123456' } });
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalled();
        });

        const { onRetry } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
        act(() => {
            onRetry();
        });

        await waitFor(() => {
            expect(useSourceReloadMock).toHaveBeenCalled();
        });
    });

    it('onModalStateChange calls onModalStateChangeExternalNotification when provided', async () => {
        const mockOnModalStateChange = jest.fn();
        const sourcesInput = [mockSource({ type: 'dam' })];
        mockRequestSources.mockResolvedValueOnce(sourcesInput);

        renderComponent({ onModalStateChange: mockOnModalStateChange });
        await waitFor(() => {
            expect(RBI.ResourceBrowserInput).toHaveBeenCalled();
        });

        const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];

        // Test opening modal
        act(() => {
            onModalStateChange(true);
        });

        await waitFor(() => {
            expect(mockOnModalStateChange).toHaveBeenCalledWith(true);
        });

        // Test closing modal
        act(() => {
            onModalStateChange(false);
        });

        await waitFor(() => {
            expect(mockOnModalStateChange).toHaveBeenCalledWith(false);
        });

        expect(mockOnModalStateChange).toHaveBeenCalledTimes(2);
    });

    describe('Resource browser plugin', () => {
        let PluginRenderSpy: jest.SpyInstance;
        beforeEach(() => {
            //@ts-ignore
            PluginRenderSpy = jest.spyOn(Plugin.PluginRender, 'render');
        });
        afterEach(() => {
            PluginRenderSpy.mockRestore();
        });

        it('Will default to a non plugin based render for initial load and selection of first source', async () => {
            const sourcesInput = [mockSource({ type: 'dam' }), mockSource({ type: 'matrix' })];
            mockRequestSources.mockResolvedValue(sourcesInput);
            renderComponent();

            // Will render a default with no selected source
            await waitFor(() => {
                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: true,
                        type: null,
                        plugin: null,
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: false,
                        type: 'dam',
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: false,
                        type: 'matrix',
                    }),
                    null,
                );
            });
        });

        it('Will only send render=true to the Plugin for the currently selected source', async () => {
            const sourcesInput = [mockSource({ type: 'dam' }), mockSource({ type: 'matrix' })];
            mockRequestSources.mockResolvedValue(sourcesInput);

            // Render with an input so it will default a source
            renderComponent({ value: { sourceId: sourcesInput[0].id, resourceId: '123456' } });

            // Will render a default with no selected source
            await waitFor(() => {
                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: false,
                        type: null,
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: true,
                        type: 'dam',
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: false,
                        type: 'matrix',
                    }),
                    null,
                );
            });

            // renderComponent({ value: { sourceId: sourcesInput[0].id, resourceId: '123456' } });
        });

        it('Will match plugin to render based on type', async () => {
            const sourcesInput = [mockSource({ type: 'dam' }), mockSource({ type: 'matrix' })];
            const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
            mockRequestSources.mockResolvedValue(sourcesInput);
            renderComponent();

            // Modify the source so the plugin object comparison doesnt match
            calculatedSources[0] = {
                ...calculatedSources[0],
                plugin: {
                    // @ts-ignore
                    test: 'test',
                    ...calculatedSources[0].plugin,
                },
            };

            // Get the provided callback
            const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
            // Invoke it
            act(() => {
                setSource(calculatedSources[0]);
            });

            // Will render a default with no selected source
            await waitFor(() => {
                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: true,
                        type: null,
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: true,
                        type: 'dam',
                    }),
                    null,
                );

                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: false,
                        type: 'matrix',
                    }),
                    null,
                );
            });
        });

        it('switching source updates plugin and does not use the old plugin', async () => {
            const damSource = mockSource({ type: 'dam', id: '1' });
            const matrixSource = mockSource({ type: 'matrix', id: '2' });

            const pluginA = { ...mockDamPlugin, type: 'dam' };
            const pluginB = { ...mockDamPlugin, type: 'matrix' };

            mockRequestSources.mockResolvedValueOnce([damSource, matrixSource]);

            renderComponent();

            const { setSource } = (RBI.ResourceBrowserInput as jest.Mock).mock.calls[0][0];

            act(() => {
                setSource({ ...damSource, plugin: pluginA });
            });

            await waitFor(() => {
                expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                    expect.objectContaining({
                        plugin: pluginA,
                    }),
                    expect.any(Object),
                );
            });

            (PluginRenderSpy as unknown as jest.Mock).mockClear();

            act(() => {
                setSource({ ...matrixSource, plugin: pluginB });
            });

            await waitFor(() => {
                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        plugin: pluginB,
                    }),
                    expect.any(Object),
                );
                expect(PluginRenderSpy).not.toHaveBeenCalledWith(
                    expect.objectContaining({
                        plugin: pluginA,
                    }),
                    expect.any(Object),
                );
            });
        });

        it('When resource browser is called with a value containing an alias source, Plugin is called with the original source not the alias value', async () => {
            const originalSource = mockSource({
                id: 'original-source-id',
                name: 'Original Source',
                type: 'dam',
                aliases: ['alias-source-id'],
            });
            const aliasSourceId = 'alias-source-id';

            mockRequestSources.mockResolvedValueOnce([originalSource]);

            // Create a value that uses the alias source ID
            const valueWithAlias = {
                sourceId: aliasSourceId,
                resourceId: '123456',
            };

            renderComponent({ value: valueWithAlias });

            await waitFor(() => {
                expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
                    expect.objectContaining({
                        value: valueWithAlias,
                        source: calculateExpectedSource(originalSource), // Should be the original source, not alias
                        plugin: mockDamPlugin,
                    }),
                    {},
                );
            });

            // Verify that the PluginRender is called with the original source
            await waitFor(() => {
                expect(PluginRenderSpy).toHaveBeenCalledWith(
                    expect.objectContaining({
                        render: true,
                        type: 'dam',
                        source: calculateExpectedSource(originalSource), // Should be the original source
                        isOtherSourceValue: false,
                    }),
                    null,
                );
            });
        });
    });
});
