import React from 'react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderInAppContext } from '@theorchard/suite-testing';
import { assetReport } from 'lib/test/mockAssetReport';
import AssetReportTable from 'src/components/assetReport/assetReportTable';
import { ASSET_STATUS } from 'src/components/assetReport/types';
import type { Product } from 'src/components/assetReport/types';

const defaultProps = {
    loading: false,
    products: assetReport,
    pageNumber: 0,
    pageSize: 10,
    numProducts: 2,
    onPageChange: jest.fn(),
    onPageSizeChange: jest.fn(),
    onSort: jest.fn(),
};

const renderTable = (overrides = {}) =>
    renderInAppContext(<AssetReportTable {...defaultProps} {...overrides} />);

describe('<AssetReportTable />', () => {
    test('matches snapshot', () => {
        const component = renderTable();
        expect(component).toMatchSnapshot();
    });

    test('renders product names', () => {
        renderTable();
        expect(screen.getByText('test 1')).toBeInTheDocument();
        expect(screen.getByText('test 2')).toBeInTheDocument();
    });

    test('renders product codes', () => {
        renderTable();
        expect(screen.getByText('test1')).toBeInTheDocument();
        expect(screen.getByText('test2')).toBeInTheDocument();
    });

    test('renders artwork status for each product', () => {
        const { container } = renderTable();
        const successStatuses = container.querySelectorAll('.variant-success');
        const errorStatuses = container.querySelectorAll('.variant-error');
        expect(successStatuses.length).toBeGreaterThan(0);
        expect(errorStatuses.length).toBeGreaterThan(0);
    });

    test('renders empty table when products is null', () => {
        renderTable({ products: null });
        expect(screen.queryByText('test 1')).not.toBeInTheDocument();
    });

    test('applies error-row class to rows with failed artwork', () => {
        const { container } = renderTable();
        const errorRows = container.querySelectorAll('.error-row');
        expect(errorRows.length).toBeGreaterThan(0);
    });

    test('applies error-row class to rows with failed tracks', () => {
        const productsWithFailedTrackOnly: Product[] = [
            {
                productName: 'track fail product',
                productCode: 'tfp1',
                artwork: { status: ASSET_STATUS.Success, errors: null },
                tracks: [
                    {
                        trackName: 'bad track',
                        status: ASSET_STATUS.Fail,
                        errors: ['error'],
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: productsWithFailedTrackOnly,
            numProducts: 1,
        });
        expect(container.querySelector('.error-row')).toBeInTheDocument();
    });

    test('applies unclickable class to rows without failures', () => {
        const successProducts: Product[] = [
            {
                productName: 'good product',
                productCode: 'gp1',
                artwork: { status: ASSET_STATUS.Success, errors: null },
                tracks: [
                    {
                        trackName: 'good track',
                        status: ASSET_STATUS.Success,
                        errors: null,
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: successProducts,
            numProducts: 1,
        });
        expect(container.querySelector('.unclickable')).toBeInTheDocument();
        expect(container.querySelector('.error-row')).not.toBeInTheDocument();
    });

    test('expands row to show errors on click', async () => {
        const { container } = renderTable();
        const errorRow = container.querySelector('.error-row');
        expect(errorRow).toBeInTheDocument();
        userEvent.click(errorRow!);
        expect(
            screen.getByText(
                'Audio file has 1 channel(s). Channels must equal 2.'
            )
        ).toBeInTheDocument();
    });

    test('collapses expanded row on second click', async () => {
        const { container } = renderTable();
        const errorRow = container.querySelector('.error-row');
        userEvent.click(errorRow!);
        expect(
            screen.getByText(
                'Audio file has 1 channel(s). Channels must equal 2.'
            )
        ).toBeInTheDocument();
        userEvent.click(errorRow!);
        expect(
            screen.queryByText(
                'No audio file uploaded with the filename test-1-track-2.wav'
            )
        ).not.toBeInTheDocument();
    });

    test('computes track status as InProgress when any track is InProgress', () => {
        const inProgressProducts: Product[] = [
            {
                productName: 'in progress product',
                productCode: 'ipp1',
                artwork: { status: ASSET_STATUS.Success, errors: null },
                tracks: [
                    {
                        trackName: 'pending track',
                        status: ASSET_STATUS.InProgress,
                        errors: null,
                    },
                    {
                        trackName: 'done track',
                        status: ASSET_STATUS.Success,
                        errors: null,
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: inProgressProducts,
            numProducts: 1,
        });
        expect(container.querySelector('.LoadingSpinner')).toBeInTheDocument();
    });

    test('optimistically shows a loader for an asset whose file is being re-uploaded', () => {
        const failedProducts: Product[] = [
            {
                productName: 'reuploaded product',
                productCode: 'rup1',
                artwork: {
                    status: ASSET_STATUS.Fail,
                    errors: ['No image uploaded with the filename art.jpg'],
                    filename: 'art.jpg',
                },
                tracks: [
                    {
                        trackName: 'ok track',
                        status: ASSET_STATUS.Success,
                        errors: null,
                        filename: 'track.wav',
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: failedProducts,
            numProducts: 1,
            uploadingFilenames: ['art.jpg'],
        });
        expect(container.querySelector('.LoadingSpinner')).toBeInTheDocument();
    });

    test('computes track status as Skipped when tracks are skipped and none failed', () => {
        const skippedProducts: Product[] = [
            {
                productName: 'skipped product',
                productCode: 'sp1',
                artwork: { status: ASSET_STATUS.Success, errors: null },
                tracks: [
                    {
                        trackName: 'skipped track',
                        status: ASSET_STATUS.Skipped,
                        errors: null,
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: skippedProducts,
            numProducts: 1,
        });
        expect(container.querySelector('.variant-warning')).toBeInTheDocument();
    });

    test('computes artwork status as Fail when artwork is Skipped with errors', () => {
        const skippedWithErrors: Product[] = [
            {
                productName: 'skipped error product',
                productCode: 'sep1',
                artwork: {
                    status: ASSET_STATUS.Skipped,
                    errors: ['Missing artwork'],
                },
                tracks: [
                    {
                        trackName: 'ok track',
                        status: ASSET_STATUS.Success,
                        errors: null,
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: skippedWithErrors,
            numProducts: 1,
        });
        expect(container.querySelector('.error-row')).toBeInTheDocument();
    });

    test('computes artwork status as Skipped when artwork is Skipped without errors', () => {
        const skippedNoErrors: Product[] = [
            {
                productName: 'skipped no error',
                productCode: 'sne1',
                artwork: {
                    status: ASSET_STATUS.Skipped,
                    errors: null,
                },
                tracks: [
                    {
                        trackName: 'ok track',
                        status: ASSET_STATUS.Success,
                        errors: null,
                    },
                ],
            },
        ];
        const { container } = renderTable({
            products: skippedNoErrors,
            numProducts: 1,
        });
        expect(container.querySelector('.variant-warning')).toBeInTheDocument();
        expect(container.querySelector('.error-row')).not.toBeInTheDocument();
    });
});
