import React from 'react';
import { screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { contractDetails as contractDetailResponse } from 'src/__fixtures__/graphql/contract-details';
import { contractPartyList } from 'src/__fixtures__/graphql/contract-party';
import * as storeList from 'src/__fixtures__/graphql/store-list-response.json';
import * as transactionTypeList from 'src/__fixtures__/graphql/transaction-types-response.json';
import { AbacusContractPartyTargetType } from 'src/apollo/definitions/globalTypes';
import * as contractQuery from 'src/apollo/queries/contract';
import * as contractAdvanceQuery from 'src/apollo/queries/contract-advance';
import * as contractPartyQuery from 'src/apollo/queries/contract-party';
import * as productQuery from 'src/apollo/queries/product-search';
import * as storesQuery from 'src/apollo/queries/stores';
import * as trackQuery from 'src/apollo/queries/track-search';
import * as transactionTypeQuery from 'src/apollo/queries/transaction-types';
import * as vendorQuery from 'src/apollo/queries/vendor-search';
import { NO_ADVANCE_RESULTS as NO_ADVANCES_FOUND } from 'src/apollo/type-constants/advance';
import { NO_CONTRACT_FLOWTHROUGH_MSG } from 'src/apollo/type-constants/contract-flowthrough';
import { ContractDetail } from 'src/components/contract-detail/contract-detail';
import { CONTRIBUTORS_HEADERS } from 'src/components/contract-detail/nr-contract-contributors';
import { ABACUS_PROFILE, USER_FEATURES } from 'src/constants';
import type { GetStoresQuery } from 'src/apollo/queries/__generated__/stores';
import type { GetTransactionTypesWithGroupsQuery } from 'src/apollo/queries/transaction-types/__generated__/transaction-type-with-groups';
import type { AbacusContract } from 'src/types/abacus-contract';

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useParams: jest.fn().mockReturnValue({ contractId: 1 }),
}));

const mockIdentity = {
    id: 'Jane User',
    profileType: ABACUS_PROFILE,
    features: {
        [USER_FEATURES.ABACUS_NR_CONTRACT_PAGE_REDESIGN]: false,
        [USER_FEATURES.ABACUS_CONTRACT_EARNINGS_TRANSFERS]: true,
    },
};

describe('<ContractDetail/>', () => {
    const contractMock =
        contractDetailResponse.abacusContract as AbacusContract;
    const mockLabel = {
        orchardLabel: { id: { vendorId: 123 }, name: 'Some Name' },
    };
    const mockProduct = {
        productByUpc: { productName: 'Product Exception Name', upc: '123' },
    };
    const mockTrack = {
        data: { globalSoundRecordingByIsrc: { name: 'Track Exception Name' } },
    };
    let useContractSpy: jest.SpyInstance;
    let useStoreSpy: jest.SpyInstance;
    let useTransactionTypeSpy: jest.SpyInstance;

    const render = (identity = mockIdentity) =>
        renderInAppContext(<ContractDetail />, { identity });

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContract')
            .mockReturnValue({
                data: { abacusContract: contractMock },
                error: undefined,
                loading: false,
            });
        jest.spyOn(contractPartyQuery, 'useContractPartyList').mockReturnValue({
            data: contractPartyList,
            error: undefined,
            loading: false,
        });
        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,
        });
        useStoreSpy = jest.spyOn(storesQuery, 'useStoreList').mockReturnValue({
            data: storeList as GetStoresQuery,
            loading: false,
        });
        useTransactionTypeSpy = jest
            .spyOn(transactionTypeQuery, 'useTransactionTypesWithGroups')
            .mockReturnValue({
                data: transactionTypeList as GetTransactionTypesWithGroupsQuery,
                loading: false,
            });
    });

    it('renders Renewal Rules section', () => {
        const { container } = render();
        expect(container).toBeDefined();

        expect(
            screen.getByText('Current Period & Renewal Rules')
        ).toBeDefined();
    });

    it('renders signing entity and paid by', () => {
        render();
        expect(screen.getByText('Paid By')).toBeDefined();
        expect(screen.getByText('Signing Entity')).toBeDefined();
    });

    it('requests contract detail on render', () => {
        render();
        expect(useContractSpy).toHaveBeenCalled();
    });

    it('requests store list on render', () => {
        render();
        expect(useStoreSpy).toHaveBeenCalled();
    });

    it('requests transaction type list on render', () => {
        render();
        expect(useTransactionTypeSpy).toHaveBeenCalled();
    });

    it('renders "Contract Mechanical Deductions" section', () => {
        render();
        expect(screen.getByText('Mechanical Deductions')).toBeDefined();
    });

    it('renders "Physical Reserves" section', () => {
        render();
        expect(screen.getByText('Physical Reserves')).toBeDefined();
    });

    it('renders "Notes" section', () => {
        render();
        expect(screen.getByText('Additional Notes')).toBeDefined();
    });

    it('renders "Contract Summary" section', () => {
        render();
        expect(screen.getByText('Contract Summary')).toBeDefined();
    });

    it('renders "Contract Flowthrough" section', () => {
        render();
        expect(screen.getByText('Flowthrough')).toBeInTheDocument();
        expect(screen.getByText(NO_CONTRACT_FLOWTHROUGH_MSG)).toBeDefined();
    });

    it('renders "Earnings Transfers" section when FF enabled', () => {
        render();
        expect(screen.getByText('Transfer of Earnings')).toBeInTheDocument();
    });

    it('does not render "Earnings Transfers" section when FF off', () => {
        render({
            ...mockIdentity,
            features: {
                [USER_FEATURES.ABACUS_CONTRACT_EARNINGS_TRANSFERS]: false,
            },
        });
        expect(
            screen.queryByText('Transfer of Earnings')
        ).not.toBeInTheDocument();
    });

    it('renders "Advances" section', () => {
        jest.spyOn(
            contractAdvanceQuery,
            'useContractAdvancesPendingList'
        ).mockReturnValue({
            data: {
                abacusContractAdvancesPending: { items: [], totalCount: 0 },
            },
            loading: false,
            error: undefined,
        });
        jest.spyOn(
            contractAdvanceQuery,
            'useContractAdvancesPaidList'
        ).mockReturnValue({
            data: { abacusContractAdvancesPaid: { items: [], totalCount: 0 } },
            loading: false,
            error: undefined,
        });
        jest.spyOn(
            contractAdvanceQuery,
            'useContractAdvancesByStatusList'
        ).mockReturnValue({
            data: {
                abacusContract: {
                    contractId: '1',
                    contractAdvances: { items: [], totalCount: 0 },
                },
            },
            loading: false,
            error: undefined,
        });
        render();
        expect(screen.getByText('Advances')).toBeDefined();
        expect(screen.getByTestId('add-contract-advance')).toBeDefined();
        expect(screen.getByText(NO_ADVANCES_FOUND)).toBeDefined();
    });
});

describe('<NrContractDetail/>', () => {
    const mockIdentity = {
        id: 'Jane User',
        profileType: ABACUS_PROFILE,
        features: {
            [USER_FEATURES.ABACUS_NR_CONTRACT_PAGE_REDESIGN]: false,
        },
    };
    const contractMock = contractDetailResponse.abacusContract;
    let useContractSpy: jest.SpyInstance;
    let useContractPartyListSpy: jest.SpyInstance;

    const render = (identity = mockIdentity) =>
        renderInAppContext(<ContractDetail />, { identity });

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContract')
            .mockReturnValue({
                data: {
                    abacusContract: {
                        ...contractMock,
                        contractType: 'neighbouring_rights',
                    } as AbacusContract,
                },
                error: undefined,
                loading: false,
            });
        useContractPartyListSpy = jest
            .spyOn(contractPartyQuery, 'useContractPartyList')
            .mockReturnValue({
                data: contractPartyList,
                error: undefined,
                loading: false,
            });
    });

    it('renders', () => {
        const { container } = render();
        expect(container).toBeDefined();

        expect(screen.queryByText('Distribution')).toBeNull();
        expect(screen.getByText('Additional Notes')).toBeDefined();
    });

    it('requests contract detail on render', () => {
        render();
        expect(useContractSpy).toHaveBeenCalled();
    });

    it('requests contract party list on render', () => {
        render();
        expect(useContractPartyListSpy).toHaveBeenCalledWith({
            contractId: 1,
            limit: 100,
            offset: 0,
            targetType: AbacusContractPartyTargetType.CONTRIBUTOR,
        });
    });

    it('renders the contributors table header', () => {
        render();
        const headersText = screen
            .getAllByTestId('tableBasicHeaderCellTestId')
            .map(th => th.textContent);
        expect(headersText).toEqual(CONTRIBUTORS_HEADERS);
    });

    it('hides "Mechanical Deductions" and "Physical Reserves" sections', () => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContract')
            .mockReturnValue({
                data: {
                    abacusContract: {
                        ...contractMock,
                        contractType: 'neighbouring_rights',
                    } as AbacusContract,
                },
                error: undefined,
                loading: false,
            });

        render();
        expect(screen.queryByText('Mechanical Deductions')).toBeNull();
        expect(screen.queryByText('Physical Reserves')).toBeNull();
    });

    it('does not render "Contract Flowthrough" section', () => {
        render();
        expect(screen.queryByText('Flowthrough')).not.toBeInTheDocument();
    });

    it('renders old "Terms" section when FF is disabled', () => {
        render({
            ...mockIdentity,
            features: {
                [USER_FEATURES.ABACUS_NR_CONTRACT_PAGE_REDESIGN]: false,
            },
        });
        expect(screen.getByText('Terms')).toBeDefined();
        expect(screen.queryByTestId('nr-terms-addButton')).toBeNull();
    });

    it('renders new "Terms" section when FF is enabled', () => {
        render({
            ...mockIdentity,
            features: {
                [USER_FEATURES.ABACUS_NR_CONTRACT_PAGE_REDESIGN]: true,
            },
        });
        expect(screen.getByText('Terms')).toBeDefined();
        expect(screen.getByTestId('nr-terms-addButton')).toBeDefined();
    });
});
