import React from 'react';
import { screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { MemoryRouter, Route } from 'react-router-dom';
import { trackAttachments } from 'src/__fixtures__/graphql/track-search-response';
import { useTransactionTypeGroups } from 'src/apollo/queries/transaction-types';
import { CONTRACT_TERM_TYPES, USER_FEATURES } from 'src/constants';
import { ContractTermContext } from 'src/contexts/contract-term-context';
import * as contractTermTracksHook from 'src/hooks/contract-term-tracks';
import { ContractTermDetailFormattedPresentational } from 'src/types/contract-term';
import ContractTermsDetailPresentational, {
    ContractTermsDetailPropTypes,
    formatExceptions,
    retrieveDefaultLabelShare,
} from '../contract-terms-detail-presentational';

jest.mock('src/apollo/queries/transaction-types', () => ({
    useTransactionTypeGroups: jest.fn(),
}));

jest.mock('src/hooks/contract-term-tracks', () => ({
    useContractTermTracks: jest.fn(),
}));

const componentInContext = (
    contextValues: any,
    props: any,
    defaultProps: any
) => (
    <MemoryRouter initialEntries={['/contracts/123']}>
        <ContractTermContext.Provider value={contextValues}>
            <Route path="/contracts/:contractId">
                <ContractTermsDetailPresentational
                    {...defaultProps}
                    {...props}
                />
            </Route>
        </ContractTermContext.Provider>
    </MemoryRouter>
);

describe('<ContractTermsDetailPresentational />', () => {
    const contextValues = {
        state: {
            stores: {
                101: 'Store A',
                202: 'Store B',
            },
            transactionTypes: [
                {
                    __typename: 'TransactionType',
                    txnTypeCode: 'ALB',
                    txnTypeId: '1',
                    txnTypeName: 'Album',
                    transactionTypeGroup: {
                        __typename: 'TransactionTypeGroup',
                        referenceTransactionTypeGroupId: '10',
                        transactionTypeGroupName: 'Album Group',
                    },
                },
            ],
        },
        dispatch: jest.fn(),
    };

    const defaultMockIdentity = {
        features: {
            [USER_FEATURES.ABACUS_IMPROVE_TRACK_SEARCH]: false,
        },
    };

    const render = (
        contextValues: any,
        identity: any = {},
        props: any,
        defaultProps: any
    ) =>
        renderInAppContext(
            componentInContext(contextValues, props, defaultProps),
            {
                identity,
            }
        );

    const defaultProps: ContractTermsDetailPropTypes = {
        contractTerms: [],
        termType: CONTRACT_TERM_TYPES.LABEL,
    };

    beforeEach(() => {
        (useTransactionTypeGroups as jest.Mock).mockReturnValue({
            data: null,
            loading: true,
        });
        (
            contractTermTracksHook.useContractTermTracks as jest.Mock
        ).mockReturnValue({
            tracks: trackAttachments.abacusContractTerm.attachmentsObjects,
            loading: false,
            error: null,
        });
    });

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

    const setup = (
        contextValue = contextValues,
        props: Partial<ContractTermsDetailPropTypes> = {},
        propsDefault: any = defaultProps,
        identity: any = defaultMockIdentity
    ) => {
        return render(contextValue, identity, props, propsDefault);
    };

    it('renders without crashing', () => {
        const { container } = setup();
        expect(container).toBeInTheDocument();
    });

    it('shows "No Terms" message when contractTerms array is empty', () => {
        setup();
        expect(
            screen.getByTestId('contractTermsNoTermsMessage')
        ).toBeInTheDocument();

        expect(
            screen.getByText(/No Label Terms have been defined/)
        ).toBeInTheDocument();
    });

    describe('when contractTerms array is NOT empty', () => {
        beforeEach(() => {
            (useTransactionTypeGroups as jest.Mock).mockReturnValue({
                data: {
                    transactionTypeGroups: [
                        {
                            __typename: 'TransactionTypeGroup',
                            referenceTransactionTypeGroupId: '10',
                            transactionTypeGroupName: 'Album Group',
                        },
                    ],
                },
                loading: false,
            });
        });

        it('renders term blocks for terms with labelIds', () => {
            const exampleTerms = [
                {
                    attachments: ['83418'],
                    attachmentsRelations: null,
                    conditions: [],
                    contract: { contractId: '544622' },
                    contractTermId: '80354',
                    contractTermName: 'Term With Label',
                    isBaseTerm: true,
                    termType: 'label',
                    contractTermSchedules: null,
                },
            ];

            setup(
                contextValues,
                { contractTerms: exampleTerms },
                defaultProps,
                defaultMockIdentity
            );

            expect(
                screen.queryByTestId('contractTermsNoTermsMessage')
            ).not.toBeInTheDocument();

            const headers = screen.getAllByTestId(
                'labelInfoBlock-detail-header-test'
            );
            expect(headers).toHaveLength(1);
            expect(headers[0]).toHaveTextContent('(83418)');
        });

        it('renders a default header when a term has no labelIds', () => {
            const exampleTerms = [
                {
                    attachments: [],
                    attachmentsRelations: {
                        labelIds: [],
                        upcs: null,
                        contributors: null,
                    },
                    conditions: [],
                    contract: { contractId: '544622' },
                    contractTermId: '80355',
                    contractTermName: 'Term Without Label',
                    isBaseTerm: true,
                    termType: 'track',
                    contractTermSchedules: null,
                },
            ];

            setup(
                contextValues,
                { contractTerms: exampleTerms },
                { ...defaultProps, termType: 'track' },
                defaultMockIdentity
            );

            expect(
                screen.queryByTestId('contractTermsNoTermsMessage')
            ).not.toBeInTheDocument();
            expect(screen.getByText('Term Without Label')).toBeInTheDocument();
        });

        describe('with FF enabled', () => {
            it('renders term blocks with FF enabled', () => {
                const mockIdentity = {
                    features: {
                        [USER_FEATURES.ABACUS_IMPROVE_TRACK_SEARCH]: true,
                    },
                };
                const exampleTerms = [
                    {
                        attachments: ['isrc1', 'isrc2'],
                        attachmentsRelations: {
                            contributors: null,
                            labelIds: ['49780'],
                            upcs: null,
                        },
                        conditions: [],
                        contract: { contractId: '544622' },
                        contractTermId: '80354',
                        contractTermName: 'FF Term',
                        isBaseTerm: true,
                        termType: 'track',
                        contractTermSchedules: null,
                    },
                ];
                const defaultTrackProps = {
                    contractTerms: exampleTerms,
                    termType: CONTRACT_TERM_TYPES.TRACK,
                };
                setup(
                    contextValues,
                    { contractTerms: exampleTerms },
                    defaultTrackProps,
                    mockIdentity
                );

                expect(
                    screen.queryByTestId('contractTermsNoTermsMessage')
                ).not.toBeInTheDocument();
                expect(
                    screen.getByTestId('labelInfoBlock-detail-header-test')
                ).toBeInTheDocument();
                expect(
                    screen.getByTestId('moreDetailsButton')
                ).toBeInTheDocument();
                expect(
                    screen.getByTestId(
                        'labelInfoBlock-detail-subHeaderContainer'
                    )
                ).toHaveTextContent('2 tracks');
            });
        });
    });

    it('displays a heading with the correct termType text', () => {
        setup();
        expect(screen.getByTestId('parentTermType').innerHTML).toContain(
            'Label Terms'
        );
    });

    describe('retrieveDefaultLabelShare', () => {
        it('returns null if no conditions exist', () => {
            expect(retrieveDefaultLabelShare([])).toBeNull();
        });

        it('returns null if no "All" country and service conditions exist', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['UK'],
                    service: ['Streaming'],
                    labelShare: 85,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
            ];
            expect(retrieveDefaultLabelShare(conditions)).toBeNull();
        });

        it('returns labelShare if only one "All" condition exists', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
            ];
            expect(retrieveDefaultLabelShare(conditions)).toBe(70);
        });

        it('returns array of unique labelShares if multiple "All" conditions exist', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 80,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(retrieveDefaultLabelShare(conditions)).toEqual([70, 80]);
        });

        it('returns a single number if multiple "All" conditions have the same labelShare', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(retrieveDefaultLabelShare(conditions)).toBe(70);
        });

        it('ignores conditions where labelShare is null', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: null,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 75,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(retrieveDefaultLabelShare(conditions)).toBe(75);
        });
    });

    describe('formatExceptions', () => {
        it('returns an empty array if there are no conditions', () => {
            expect(formatExceptions([], null)).toEqual([]);
        });

        it('excludes "All" conditions from exceptions', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['UK'],
                    service: ['Streaming'],
                    labelShare: 80,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(formatExceptions(conditions, 70)).toEqual([
                { country: ['UK'], service: ['Streaming'], labelShare: 80 },
            ]);
        });

        it('includes only conditions where labelShare differs from defaultLabelShare', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['UK'],
                    service: ['Streaming'],
                    labelShare: 80,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
                {
                    country: ['FR'],
                    service: ['Download'],
                    labelShare: 70,
                    priority: 3,
                    transactionType: [],
                    id: 3,
                },
            ];
            expect(formatExceptions(conditions, 70)).toEqual([
                { country: ['UK'], service: ['Streaming'], labelShare: 80 },
            ]);
        });

        it('works when defaultLabelShare is an array and excludes matches', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 80,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
                {
                    country: ['UK'],
                    service: ['Streaming'],
                    labelShare: 85,
                    priority: 3,
                    transactionType: [],
                    id: 3,
                },
                {
                    country: ['FR'],
                    service: ['Download'],
                    labelShare: 80,
                    priority: 4,
                    transactionType: [],
                    id: 4,
                },
            ];
            expect(formatExceptions(conditions, [70, 80])).toEqual([
                { country: ['UK'], service: ['Streaming'], labelShare: 85 },
            ]);
        });

        it('returns an empty array if all conditions match the defaultLabelShare', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['All'],
                    service: ['All'],
                    labelShare: 70,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['FR'],
                    service: ['Download'],
                    labelShare: 70,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(formatExceptions(conditions, 70)).toEqual([]);
        });

        it('filters out conditions where labelShare is null', () => {
            const conditions: ContractTermDetailFormattedPresentational[] = [
                {
                    country: ['UK'],
                    service: ['Streaming'],
                    labelShare: null,
                    priority: 1,
                    transactionType: [],
                    id: 1,
                },
                {
                    country: ['FR'],
                    service: ['Download'],
                    labelShare: 85,
                    priority: 2,
                    transactionType: [],
                    id: 2,
                },
            ];
            expect(formatExceptions(conditions, 70)).toEqual([
                { country: ['FR'], service: ['Download'], labelShare: 85 },
            ]);
        });
    });
});
