import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import * as isrcEntities from 'src/data/queries/isrcEntities/isrcEntities';
import { useGetClashedCompositions } from '../helpers';
import type { IsrcEntities } from 'src/data/queries/isrcEntities/isrcEntities';
import type { AssociatedSoundRecording } from 'src/types';

const VENDOR_ID = 7123;

const recording = (isrc: string): AssociatedSoundRecording => ({
    id: `gsr-${isrc}`,
    isrc,
    name: isrc,
    performers: [],
    writers: [],
    isLsr: false,
});

const clashOn = (isrc: string, compositionId: string): IsrcEntities => ({
    isrc,
    compositions: {
        compositions: [{ id: compositionId, vendor: { vendorId: VENDOR_ID } }],
    },
});

describe('useGetClashedCompositions', () => {
    let fetchIsrcEntities: jest.Mock;

    const Harness: React.FC<{
        currentCompositionId?: string | null;
        recordings: AssociatedSoundRecording[];
    }> = ({ currentCompositionId = null, recordings }) => {
        const clashed = useGetClashedCompositions(
            currentCompositionId,
            recordings,
            VENDOR_ID
        );

        return <div data-testid="clashed">{clashed ?? 'none'}</div>;
    };

    const mockEntities = (entities: Record<string, IsrcEntities>) => {
        fetchIsrcEntities = jest.fn(
            async (isrc: string) =>
                await Promise.resolve(entities[isrc] ?? null)
        );
        jest.spyOn(isrcEntities, 'useIsrcEntitiesFetcher').mockReturnValue(
            fetchIsrcEntities
        );
    };

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

    test('looks up every associated recording, not just the last one', async () => {
        mockEntities({});

        renderInAppContext(
            <Harness
                recordings={[
                    recording('AAA111111111'),
                    recording('BBB222222222'),
                    recording('CCC333333333'),
                ]}
            />
        );

        await waitFor(() => expect(fetchIsrcEntities).toHaveBeenCalledTimes(3));
        expect(fetchIsrcEntities).toHaveBeenCalledWith('AAA111111111');
        expect(fetchIsrcEntities).toHaveBeenCalledWith('BBB222222222');
        expect(fetchIsrcEntities).toHaveBeenCalledWith('CCC333333333');
    });

    test('finds a clash that sits on a recording other than the first', async () => {
        mockEntities({
            CCC333333333: clashOn('CCC333333333', 'clashed-composition-id'),
        });

        renderInAppContext(
            <Harness
                recordings={[
                    recording('AAA111111111'),
                    recording('BBB222222222'),
                    recording('CCC333333333'),
                ]}
            />
        );

        await waitFor(() =>
            expect(screen.getByTestId('clashed')).toHaveTextContent(
                'clashed-composition-id'
            )
        );
    });

    test('does not clash with the composition being edited', async () => {
        mockEntities({
            AAA111111111: clashOn('AAA111111111', 'current-composition-id'),
        });

        renderInAppContext(
            <Harness
                currentCompositionId="current-composition-id"
                recordings={[recording('AAA111111111')]}
            />
        );

        await waitFor(() => expect(fetchIsrcEntities).toHaveBeenCalled());
        expect(screen.getByTestId('clashed')).toHaveTextContent('none');
    });

    test('reports no clash when nothing is associated', async () => {
        mockEntities({
            AAA111111111: clashOn('AAA111111111', 'clashed-composition-id'),
        });

        renderInAppContext(<Harness recordings={[]} />);

        await waitFor(() =>
            expect(screen.getByTestId('clashed')).toHaveTextContent('none')
        );
        expect(fetchIsrcEntities).not.toHaveBeenCalled();
    });
});
