import React from 'react';
import { screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { Route } from 'react-router-dom';
import * as mockLabelContract from 'src/__fixtures__/graphql/contract-terms-label-term-detail-response.json';
import * as mockProductContract from 'src/__fixtures__/graphql/contract-terms-product-term-detail-response.json';
import * as mockTrackContract from 'src/__fixtures__/graphql/contract-terms-track-term-detail-response.json';
import * as contractTermQuery from 'src/apollo/queries/contract-term';
import * as productQuery from 'src/apollo/queries/product-search';
import * as trackQuery from 'src/apollo/queries/track-search';
import * as vendorQuery from 'src/apollo/queries/vendor-search';
import ContractTermsDetail, {
    ContractTermsDetailPropTypes,
} from 'src/components/contract-detail/contract-terms-detail';
import {
    ABACUS_PROFILE,
    CONTRACT_TERM_TYPES,
    USER_FEATURES,
} from 'src/constants';
import { ContractTermContext } from 'src/contexts/contract-term-context';
import { initialTermsState } from 'src/reducers/contract-term/contract-terms-details-reducer';
import { getContractDetail } from 'src/urls/frontend-royalties';

const componentInContext = (contextValues: any, props: any) => (
    <Route path="/contract/:contractId">
        <ContractTermContext.Provider value={contextValues}>
            <ContractTermsDetail {...props} />
        </ContractTermContext.Provider>
    </Route>
);

describe('<ContractTermsDetail>', () => {
    const mockLabel = {
        orchardLabel: { id: { vendorId: 32486 }, name: 'Some Name' },
    };
    const mockProduct = {
        productByUpc: {
            productName: 'Product Track Name',
            upc: 'Mocked UPC',
        },
    };
    const mockTrack = [
        { data: { globalSoundRecordingByIsrc: { name: 'FirstTrackName' } } },
        { data: { globalSoundRecordingByIsrc: { name: 'SomeTrackName' } } },
        { data: { globalSoundRecordingByIsrc: { name: 'AwesomeTrackName' } } },
    ];
    const mockContractAttachmentsObjects = {
        abacusContractTerm: { contractTermId: '1', attachmentsObjects: [] },
    };
    const mockIdentity = {
        profileType: ABACUS_PROFILE,
        features: {
            [USER_FEATURES.ABACUS_SHOW_NR_CONTRIBUTIONS]: true,
        },
    };

    beforeEach(() => {
        jest.mock('react-router-dom', () => ({
            ...jest.requireActual('react-router-dom'),
            useParams: jest.fn().mockReturnValue({ contractId: 123 }),
        }));
        jest.spyOn(vendorQuery, 'useVendorById').mockReturnValue({
            data: mockLabel,
            getVendorById: jest.fn().mockReturnValue(mockLabel),
            loading: false,
        });

        jest.spyOn(productQuery, 'useProductByUpc').mockReturnValue({
            data: mockProduct,
            getProductByUpc: jest.fn().mockReturnValue(mockProduct),
            loading: false,
        });

        jest.spyOn(trackQuery, 'useTrackByBatchIsrc').mockReturnValue({
            data: mockTrack,
            error: false,
            loading: false,
        });
        jest.spyOn(
            contractTermQuery,
            'useContractAttachmentsObjects'
        ).mockReturnValue({
            data: mockContractAttachmentsObjects,
            error: undefined,
            loading: false,
            fetchMore: jest.fn(),
        });
    });

    afterEach(jest.restoreAllMocks);

    describe('checks to see if the shared frontend elements for any term container are rendered', () => {
        const { contractTerms } = mockLabelContract.abacusContract;

        const render = (contextValues: any, props: any, identity: any = {}) =>
            renderInAppContext(componentInContext(contextValues, props), {
                identity,
                pathname: getContractDetail('123'),
            });
        const contextValues = { state: initialTermsState, dispatch: jest.fn() };

        const props: ContractTermsDetailPropTypes = {
            contractTerms:
                contractTerms as ContractTermsDetailPropTypes['contractTerms'],
            termType: CONTRACT_TERM_TYPES.LABEL,
        };

        it('renders the ContractTermsDetailCard', () => {
            render(contextValues, props, mockIdentity);
            const contractTermsDetailCard = screen.getByTestId(
                'contractTermConditionsCard'
            );
            expect(contractTermsDetailCard).toBeDefined();
        });

        it('renders the expected headers for every term condition table', () => {
            render(contextValues, props, mockIdentity);

            const termIdColumnHeaders =
                screen.getAllByTestId('tableHeaderTermId');
            expect(termIdColumnHeaders).toBeDefined();
            termIdColumnHeaders.forEach(termIdColumnHeader => {
                expect(termIdColumnHeader).toHaveTextContent('term id');
            });

            const catalogColumnHeaders =
                screen.getAllByTestId('tableHeaderCatalog');
            expect(catalogColumnHeaders).toBeDefined();
            catalogColumnHeaders.forEach(catalogColumnHeader => {
                expect(catalogColumnHeader).toHaveTextContent('catalog');
            });

            const transactionTypeColumnHeaders = screen.getAllByTestId(
                'tableHeaderTransactionType'
            );
            expect(transactionTypeColumnHeaders).toBeDefined();
            transactionTypeColumnHeaders.forEach(transactionTypeColumHeader => {
                expect(transactionTypeColumHeader).toHaveTextContent(
                    'transaction type'
                );
            });

            const serviceColumnHeaders =
                screen.getAllByTestId('tableHeaderService');
            expect(serviceColumnHeaders).toBeDefined();
            serviceColumnHeaders.forEach(serviceColumnHeader => {
                expect(serviceColumnHeader).toHaveTextContent('service');
            });

            const countryColumnHeaders =
                screen.getAllByTestId('tableHeaderCountry');
            expect(countryColumnHeaders).toBeDefined();
            countryColumnHeaders.forEach(countryColumnHeader => {
                expect(countryColumnHeader).toHaveTextContent('country');
            });

            const labelShareColumnHeaders = screen.getAllByTestId(
                'tableHeaderLabelShare'
            );
            expect(labelShareColumnHeaders).toBeDefined();
            labelShareColumnHeaders.forEach(labelShareColumnHeader => {
                expect(labelShareColumnHeader).toHaveTextContent(
                    `label's share`
                );
            });

            const actionsColumnHeaders =
                screen.getAllByTestId('tableHeaderActions');
            expect(actionsColumnHeaders).toBeDefined();
            actionsColumnHeaders.forEach(actionsColumnHeader => {
                expect(actionsColumnHeader).toHaveTextContent('actions');
            });
        });
    });

    describe('<ContractTermsDetail> Label', () => {
        const { contractTerms } = mockLabelContract.abacusContract;

        const render = (contextValues: any, props: any, identity: any = {}) =>
            renderInAppContext(componentInContext(contextValues, props), {
                identity,
                pathname: getContractDetail('123'),
            });
        const contextValues = { state: initialTermsState, dispatch: jest.fn() };

        const props: ContractTermsDetailPropTypes = {
            contractTerms:
                contractTerms as ContractTermsDetailPropTypes['contractTerms'],
            termType: CONTRACT_TERM_TYPES.LABEL,
        };

        describe('checks to see if the label terms container elements are rendered', () => {
            it('renders the appropriate term container title', () => {
                render(contextValues, props, mockIdentity);
                const tableTitleElements = screen.queryByTestId(
                    'contractTermsContainerTitle'
                );
                expect(tableTitleElements).toBeDefined();
                expect(tableTitleElements).toHaveTextContent('label terms');
            });

            it('renders an add button with its respective add term page link', () => {
                render(contextValues, props, mockIdentity);
                const addLabelTermButton = screen.getByTestId(
                    'contractTermsAddButtonLabel'
                );
                expect(addLabelTermButton).toBeDefined();
                expect(addLabelTermButton).toHaveTextContent('+ Add');
                const addLabelTermPageLink = screen.getByTestId(
                    'contractTermsAddPageLinkLabel'
                ) as HTMLLinkElement;
                expect(addLabelTermPageLink.href).toContain(
                    'http://testurl.com/contract/123/terms/label/new'
                );
            });

            it('renders more than one label term table block', () => {
                render(contextValues, props, mockIdentity);
                const termBlocks = screen.getAllByTestId(/termBlock.*/);
                expect(termBlocks).toBeDefined();
                expect(termBlocks.length).toBeGreaterThan(1);
            });

            it('renders a no terms message when there are no terms available for label', () => {
                props.contractTerms = [];
                render(contextValues, props, mockIdentity);
                const noTermMessage = screen.getByTestId(
                    'contractTermsNoTermsMessage'
                );
                expect(noTermMessage).toBeDefined();
                expect(noTermMessage).toHaveTextContent(
                    'No Label Terms have been defined for this contract.'
                );
                props.contractTerms =
                    contractTerms as ContractTermsDetailPropTypes['contractTerms'];
            });
        });

        describe('checks to see if the label term table block is rendered with the expected data', () => {
            it('renders the table block', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1 = screen.getByTestId('termBlock0');
                expect(termBlock1).toBeDefined();
            });

            it('renders the correct contract term name', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1TermName = screen.getByTestId(
                    'termBlockContractTermName0'
                );
                expect(termBlock1TermName).toBeDefined();
                expect(termBlock1TermName).toHaveTextContent('Test term 1');
            });

            it('renders at least one label id with an expected formatting', () => {
                render(contextValues, props, mockIdentity);
                const labelId1 = screen.getByTestId(
                    'termBlockContractLabelId00'
                );
                expect(labelId1).toBeDefined();
                expect(labelId1).toHaveTextContent('Some Name (32486)');
                const labelId2 = screen.getByTestId(
                    'termBlockContractLabelId01'
                );
                expect(labelId2).toBeDefined();
                expect(labelId2).toHaveTextContent('Some Name (32487)');
            });

            it('renders the correct term id', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1TermId = screen.getByTestId(
                    'termBlockContractTermId00'
                );
                expect(termBlock1TermId).toBeDefined();
                expect(termBlock1TermId).toHaveTextContent('1958');
            });

            it('renders the correct term catalog', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1Catalog = screen.getByTestId(
                    'termBlockContractCatalog00'
                );
                expect(termBlock1Catalog).toBeDefined();
                expect(termBlock1Catalog).toHaveTextContent('All');
            });

            it('renders the correct term condition transaction type', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1TransactionType = screen.getByTestId(
                    'termBlockContractTransactionType00'
                );
                expect(termBlock1TransactionType).toBeDefined();
                expect(termBlock1TransactionType).toHaveTextContent(',');
            });

            it('renders the correct term condition service', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1Service = screen.getByTestId(
                    'termBlockContractService00'
                );
                expect(termBlock1Service).toBeDefined();
                expect(termBlock1Service).toHaveTextContent(',');
            });

            it('renders the correct term condition country', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1Country = screen.getByTestId(
                    'termBlockContractCountry00'
                );
                expect(termBlock1Country).toBeDefined();
                expect(termBlock1Country).toHaveTextContent(
                    'United States of America, Italy'
                );
            });

            it('renders the correct term condition label share', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1LabelShare = screen.getByTestId(
                    'termBlockContractLabelShare00'
                );
                expect(termBlock1LabelShare).toBeDefined();
                expect(termBlock1LabelShare).toHaveTextContent('80%');
            });

            it('renders an edit button', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1EditButton = screen.getByTestId(
                    'termBlockContractEditButton00'
                );
                expect(termBlock1EditButton).toBeDefined();
                expect(termBlock1EditButton).toHaveTextContent('Edit');

                const editLinkButton = screen.getByTestId(
                    'termBlockContractEditLink00'
                ) as HTMLLinkElement;
                expect(editLinkButton.href).toEqual(
                    'http://testurl.com/contract/123/terms/label/1958/edit'
                );
            });
        });
    });

    describe('<ContractTermsDetail> Product', () => {
        const { contractTerms } = mockProductContract.abacusContract;

        const render = (contextValues: any, props: any, identity: any = {}) =>
            renderInAppContext(componentInContext(contextValues, props), {
                identity,
                pathname: getContractDetail('123'),
            });
        const contextValues = { state: initialTermsState, dispatch: jest.fn() };

        const props: ContractTermsDetailPropTypes = {
            contractTerms:
                contractTerms as ContractTermsDetailPropTypes['contractTerms'],
            termType: CONTRACT_TERM_TYPES.PRODUCT,
        };

        describe('checks to see if the product terms container elements are rendered', () => {
            it('renders the appropriate term container title', () => {
                render(contextValues, props, mockIdentity);
                const tableTitleElements = screen.queryByTestId(
                    'contractTermsContainerTitle'
                );
                expect(tableTitleElements).toBeDefined();
                expect(tableTitleElements).toHaveTextContent('product terms');
            });

            it('renders an add button with its respective add term page link', () => {
                render(contextValues, props, mockIdentity);

                const addProductTermButton = screen.getByTestId(
                    'contractTermsAddButtonProduct'
                );
                expect(addProductTermButton).toBeDefined();
                expect(addProductTermButton).toHaveTextContent('+ Add');
                const addProductTermPageLink = screen.getByTestId(
                    'contractTermsAddPageLinkProduct'
                ) as HTMLLinkElement;
                expect(addProductTermPageLink.href).toContain(
                    'http://testurl.com/contract/123/terms/product/new'
                );
            });

            it('renders a no terms message when there are no terms available for product', () => {
                props.contractTerms = [];
                render(contextValues, props, mockIdentity);
                const noTermMessage = screen.getByTestId(
                    'contractTermsNoTermsMessage'
                );
                expect(noTermMessage).toBeDefined();
                expect(noTermMessage).toHaveTextContent(
                    'No Product Terms have been defined for this contract.'
                );
                props.contractTerms =
                    contractTerms as ContractTermsDetailPropTypes['contractTerms'];
            });
        });

        describe('checks to see if the product term table is rendered with the expected data', () => {
            it('renders the correct term catalog', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1Catalog = screen.getByTestId(
                    'termBlockContractCatalog00'
                );
                expect(termBlock1Catalog).toBeDefined();
                expect(termBlock1Catalog).toHaveTextContent(
                    'Product Track Name -7116756Product Track Name -7116757'
                );
            });
        });
    });

    describe('<ContractTermsDetail> Track', () => {
        const { contractTerms } = mockTrackContract.abacusContract;

        const render = (contextValues: any, props: any, identity: any = {}) =>
            renderInAppContext(componentInContext(contextValues, props), {
                identity,
                pathname: getContractDetail('123'),
            });
        const contextValues = {
            state: initialTermsState,
            dispatch: jest.fn(),
        };

        const props: ContractTermsDetailPropTypes = {
            contractTerms:
                contractTerms as ContractTermsDetailPropTypes['contractTerms'],
            termType: CONTRACT_TERM_TYPES.TRACK,
        };

        describe('checks to see if the track terms container elements are rendered', () => {
            it('renders the appropriate term container title', () => {
                render(contextValues, props, mockIdentity);
                const tableTitleElements = screen.queryByTestId(
                    'contractTermsContainerTitle'
                );
                expect(tableTitleElements).toBeDefined();
                expect(tableTitleElements).toHaveTextContent('track terms');
            });

            it('renders an add button with its respective add term page link', () => {
                render(contextValues, props, mockIdentity);

                const addTrackTermButton = screen.getByTestId(
                    'contractTermsAddButtonTrack'
                );
                expect(addTrackTermButton).toBeDefined();
                expect(addTrackTermButton).toHaveTextContent('+ Add');
                const addTrackTermPageLink = screen.getByTestId(
                    'contractTermsAddPageLinkTrack'
                ) as HTMLLinkElement;
                expect(addTrackTermPageLink.href).toContain(
                    'http://testurl.com/contract/123/terms/track/new'
                );
            });

            it('renders a no terms message when there are no terms available for track', () => {
                props.contractTerms = [];
                render(contextValues, props, mockIdentity);
                const noTermMessage = screen.getByTestId(
                    'contractTermsNoTermsMessage'
                );
                expect(noTermMessage).toBeDefined();
                expect(noTermMessage).toHaveTextContent(
                    'No Track Terms have been defined for this contract.'
                );
                props.contractTerms =
                    contractTerms as ContractTermsDetailPropTypes['contractTerms'];
            });
        });

        describe('checks to see if the track term table is rendered with the expected data', () => {
            it('renders the correct term catalog', () => {
                render(contextValues, props, mockIdentity);
                const termBlock1Catalog = screen.getByTestId(
                    'termBlockContractCatalog00'
                );
                expect(termBlock1Catalog).toBeDefined();
                expect(termBlock1Catalog).toHaveTextContent(
                    'AwesomeTrackName -FirstTrackName -SomeTrackName -'
                );
            });
        });
    });
});
