import React from 'react';
import { fireEvent } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import {
    getClassicalProductData,
    getMergedProduct,
    getMockProductTrack,
} from 'lib/mockProductData';
import * as AudioInfringementModalProviderModule from 'src/components/audioInfringementModalProvider/audioInfringementModalProvider';
import { Product } from 'src/data/queries/product/types';
import * as features from 'src/utils/features';
import ProductTracksTable, { CLASS_NAME, Props } from '../productTracksTable';

describe('<ProductTracksTable />', () => {
    beforeEach(() => {
        const mockIntersectionObserver = jest.fn();
        mockIntersectionObserver.mockReturnValue({
            observe: () => null,
            unobserve: () => null,
            disconnect: () => null,
        });
        window.IntersectionObserver = mockIntersectionObserver;
    });

    const defaultProps: Props = {
        product: getMergedProduct(),
        isMergedProductFullyLoaded: true,
        currentTrackId: '123456',
        onChangeCurrentTrack: jest.fn(),
        onChangeAudioStatus: jest.fn(),
        audioStatus: 0,
    };

    const renderComponent = (
        partialProduct: Partial<Product> = {},
        partialProps: Partial<Props> = {}
    ) => {
        const mockProduct = getMergedProduct({
            productId: 1,
            tracks: [
                getMockProductTrack(),
                getMockProductTrack({
                    isrc: 'abc',
                    tuid: '456789',
                    trackNumber: 2,
                }),
            ],
            ...partialProduct,
        });

        const props: Props = {
            ...defaultProps,
            ...partialProps,
            product: mockProduct,
        };

        return renderInAppContext(<ProductTracksTable {...props} />);
    };

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

    describe('renders with no errors', () => {
        test('track rows', () => {
            const { getByTestId } = renderComponent();

            const wrapperEl = getByTestId(CLASS_NAME);
            expect(wrapperEl).toBeInTheDocument();

            const gridTableHeader = wrapperEl.querySelector(
                '.GridTable-header-row'
            );
            expect(gridTableHeader).toBeInTheDocument();

            const rowsArr = wrapperEl.getElementsByClassName('GridTable-row');
            expect(rowsArr).toHaveLength(3);
        });

        test('displays classical version track name', () => {
            const productData = getClassicalProductData();
            const { getByTestId } = renderComponent(productData);

            const wrapperEl = getByTestId(CLASS_NAME);

            const trackNameColHeader =
                wrapperEl.querySelector('.col-trackName');
            const trackNameLabel = $t(
                `contentReview.productTracksTable.workMovement`
            );
            expect(trackNameColHeader).toHaveTextContent(trackNameLabel);
        });

        test('displays notices columns', () => {
            const { getByText } = renderComponent();
            expect(getByText('Notices')).toBeInTheDocument();
        });

        test('displays track version columns', () => {
            const { getByText } = renderComponent();
            expect(getByText('Track version')).toBeInTheDocument();
        });

        test('displays correct exclusivity / eligibility values', () => {
            jest.spyOn(
                features,
                'useDisplayRightsAttributesFF'
            ).mockReturnValue(true);
            const productData = getClassicalProductData();
            const { getByTestId } = renderComponent(productData);

            const wrapperEl = getByTestId(CLASS_NAME);

            const trackRightsAttributeWrapper = wrapperEl.querySelector(
                '.ProductTrackRightsAttributes-wrapper'
            );
            expect(trackRightsAttributeWrapper).toHaveTextContent(
                'Exclusive, Ineligible Offer Type'
            );
        });

        test('displays correct audio attribute values', () => {
            jest.spyOn(features, 'useDisplayAudioAttributesFF').mockReturnValue(
                true
            );
            const productData = getClassicalProductData();
            const { getByTestId } = renderComponent(productData);

            const wrapperEl = getByTestId(CLASS_NAME);

            const audioAttributeWrapper = wrapperEl.querySelector(
                '.ProductTrackAttributes-wrapper'
            );
            expect(audioAttributeWrapper).toHaveTextContent('N/A');
        });

        test('displays correct audio attribute edits values instead of initial ones', () => {
            jest.spyOn(features, 'useDisplayAudioAttributesFF').mockReturnValue(
                true
            );
            const productData = getClassicalProductData();
            const track = productData.tracks[0];
            const { getByTestId } = renderComponent({
                ...productData,
                tracks: [
                    {
                        ...track,
                        audioAttributesEdits: [
                            {
                                attribute: {
                                    id: '5',
                                    description: 'Generic Sounds',
                                },
                            },
                        ],
                    },
                ],
            });

            const wrapperEl = getByTestId(CLASS_NAME);

            const audioAttributeWrapper = wrapperEl.querySelector(
                '.ProductTrackAttributes-wrapper'
            );
            expect(audioAttributeWrapper).toHaveTextContent('Generic Sounds');
        });

        test('displays correct rights attribute values', () => {
            jest.spyOn(features, 'useDisplayAudioAttributesFF').mockReturnValue(
                true
            );
            const productData = getClassicalProductData();
            const { getByTestId } = renderComponent(productData);

            const wrapperEl = getByTestId(CLASS_NAME);

            const audioAttributeWrapper = wrapperEl.querySelector(
                '.ProductTrackAttributes-wrapper'
            );
            expect(audioAttributeWrapper).toHaveTextContent('N/A');
        });

        test('displays correct rights attribute edits values instead of initial ones', () => {
            jest.spyOn(features, 'useDisplayAudioAttributesFF').mockReturnValue(
                true
            );
            const productData = getClassicalProductData();
            const track = productData.tracks[0];
            const { getByTestId } = renderComponent({
                ...productData,
                tracks: [
                    {
                        ...track,
                        rightsAttributesEdits: [
                            {
                                attribute: {
                                    id: '6',
                                    description: 'Soundtrack',
                                },
                            },
                        ],
                    },
                ],
            });

            const wrapperEl = getByTestId(CLASS_NAME);

            const audioAttributeWrapper = wrapperEl.querySelector(
                '.cell-rightsAttribute .ProductTrackAttributes-wrapper'
            );
            expect(audioAttributeWrapper).toHaveTextContent('Soundtrack');
        });

        test('IsrcCountCell renders link to sound recording search page', () => {
            const { getByTestId, getAllByRole } = renderComponent();
            const wrapperEl = getByTestId(CLASS_NAME);
            const isrcCountCell = wrapperEl.querySelector('.cell-isrcCount');
            const isrcLinks = getAllByRole('link', { name: /12/i });
            expect(isrcCountCell).toBeInTheDocument();
            expect(
                isrcCountCell?.querySelector('.ExternalLinkGlyphIcon')
            ).toBeVisible();
            expect(isrcLinks[0]).toBeInTheDocument();
            expect(isrcLinks[1]).toBeInTheDocument();
            expect(isrcLinks[0]).toHaveAttribute(
                'href',
                '/search?term=QM6MZ2049475'
            );
            expect(isrcLinks[1]).toHaveAttribute('href', '/search?term=abc');
        });

        describe('Enhanced track audio player functionality', () => {
            const onChangeAudioStatusSpy = jest.spyOn(
                defaultProps,
                'onChangeAudioStatus'
            );
            const onChangeCurrentTrackSpy = jest.spyOn(
                defaultProps,
                'onChangeCurrentTrack'
            );

            test('plays selected audio in ProductTracksTable', () => {
                const mockTracksMetadata = [
                    {},
                    {
                        tuid: '99999',
                        trackNumber: 2,
                        trackName: 'Track XYZ 999',
                    },
                    {
                        tuid: '88888',
                        trackNumber: 3,
                        trackName: 'Track XYZ 888',
                    },
                ];
                const { container } = renderComponent({
                    tracks: [
                        getMockProductTrack(),
                        getMockProductTrack(mockTracksMetadata[1]),
                        getMockProductTrack(mockTracksMetadata[2]),
                    ],
                });
                const trackAudioBtns = container.querySelectorAll(
                    '.track-audio-button'
                );
                trackAudioBtns.forEach((trackAudioBtn, idx) => {
                    expect(trackAudioBtn).toBeTruthy();
                    if (trackAudioBtn) {
                        fireEvent.click(trackAudioBtn);
                    }
                    if (idx > 0) {
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeCurrentTrackSpy).toHaveBeenCalledWith(
                            mockTracksMetadata[idx].tuid,
                            mockTracksMetadata[idx].trackNumber
                        );
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeAudioStatusSpy).toHaveBeenCalledWith(2);
                    } else {
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeCurrentTrackSpy).not.toHaveBeenCalled();
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeAudioStatusSpy).toHaveBeenCalledWith(1);
                    }
                    onChangeAudioStatusSpy.mockClear();
                    onChangeCurrentTrackSpy.mockClear();
                });
            });

            test('conditionally pauses/ plays selected audio in ProductTracksTable', () => {
                const mockTracksMetadata = [
                    {
                        tuid: '123456',
                        trackNumber: 1,
                    },
                    {
                        tuid: '99999',
                        trackNumber: 2,
                        trackName: 'Track XYZ 999',
                    },
                    {
                        tuid: '88888',
                        trackNumber: 3,
                        trackName: 'Track XYZ 888',
                    },
                ];
                const { container } = renderComponent(
                    {
                        tracks: [
                            getMockProductTrack(),
                            getMockProductTrack(mockTracksMetadata[1]),
                            getMockProductTrack(mockTracksMetadata[2]),
                        ],
                    },
                    { audioStatus: 2, currentTrackId: '88888' }
                );
                const trackAudioBtns = container.querySelectorAll(
                    '.track-audio-button'
                );
                trackAudioBtns.forEach((trackAudioBtn, idx) => {
                    expect(trackAudioBtn).toBeTruthy();
                    if (trackAudioBtn) {
                        fireEvent.click(trackAudioBtn);
                    }
                    if (idx < 2) {
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeCurrentTrackSpy).toHaveBeenCalledWith(
                            mockTracksMetadata[idx].tuid,
                            mockTracksMetadata[idx].trackNumber
                        );
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeAudioStatusSpy).toHaveBeenCalledWith(2);
                    } else {
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeCurrentTrackSpy).not.toHaveBeenCalled();
                        // eslint-disable-next-line jest/no-conditional-expect
                        expect(onChangeAudioStatusSpy).toHaveBeenCalledWith(-1);
                    }
                    onChangeAudioStatusSpy.mockClear();
                    onChangeCurrentTrackSpy.mockClear();
                });
            });
        });

        describe('PotentailAudioInfringementModal renders', () => {
            const renderWithMockedProvider = () => {
                return renderInAppContext(
                    <AudioInfringementModalProviderModule.AudioInfringementModalProvider>
                        <ProductTracksTable {...defaultProps} />
                    </AudioInfringementModalProviderModule.AudioInfringementModalProvider>
                );
            };

            describe('provider state has data', () => {
                beforeEach(() => {
                    jest.spyOn(
                        AudioInfringementModalProviderModule,
                        'useAudioInfringementModalContext'
                    ).mockImplementation(() => ({
                        state: {
                            data: {
                                potentialAudioInfringementMatches: [],
                                crossAccountOSRConflictMatches: [],
                                trackMetadata: {
                                    trackName: 'Track XYZ 123',
                                    artists: ['Artist A'],
                                    label: 'Label A',
                                    imprint: 'Imprint A',
                                },
                            },
                        },
                        dispatch: jest.fn(),
                    }));
                });

                test('renders modal', () => {
                    const { getByTestId } = renderWithMockedProvider();

                    const modalEl = getByTestId(
                        'PotentialAudioInfringementModal'
                    );
                    expect(modalEl).toBeInTheDocument();
                });

                test('displays correct track name in modal', () => {
                    const { getByText } = renderWithMockedProvider();

                    const modalEl = getByText('Track XYZ 123');
                    expect(modalEl).toBeInTheDocument();
                });
            });

            describe('provider state has no data', () => {
                beforeEach(() => {
                    jest.spyOn(
                        AudioInfringementModalProviderModule,
                        'useAudioInfringementModalContext'
                    ).mockImplementation(() => ({
                        state: {
                            data: null,
                        },
                        dispatch: jest.fn(),
                    }));
                });

                test('does not render modal', () => {
                    const { queryByTestId } = renderWithMockedProvider();

                    const modalEl = queryByTestId(
                        'PotentialAudioInfringementModal'
                    );
                    expect(modalEl).not.toBeInTheDocument();
                });
            });
        });
    });
});
