import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { contractTerm } from 'src/__fixtures__/graphql/contract-term';
import * as storeList from 'src/__fixtures__/graphql/store-list-response.json';
import * as transactionTypeList from 'src/__fixtures__/graphql/transaction-types-response.json';
import * as updatedContractTermCondition from 'src/__fixtures__/graphql/update-contract-term-condition-response.json';
import * as contractTermMutation from 'src/apollo/mutations/contract-term';
import * as contractTermConditionMutation from 'src/apollo/mutations/contract-term-condition';
import * as contractTermQuery from 'src/apollo/queries/contract-term';
import * as storesQuery from 'src/apollo/queries/stores';
import * as transactionTypeQuery from 'src/apollo/queries/transaction-types';
import { CONTRACT_TERM_ERROR_MSG } from 'src/apollo/type-constants/contract';
import ContractTerms from 'src/components/contract-terms-form/contract-terms';
import * as contractTermValidation from 'src/utils/form-validations/contract-term-form-validation';
import type { GetStoresQuery } from 'src/apollo/queries/__generated__/stores';
import type { GetTransactionTypesQuery } from 'src/apollo/queries/transaction-types/__generated__/transaction-types';

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useParams: jest.fn().mockReturnValue({
        contractId: '123',
        contractTermId: '555',
        termType: 'terms',
    }),
}));

describe('Edit Label <ContractTerms> tests', () => {
    const updateBaseTerm = jest.fn().mockResolvedValue([]);
    const updateLabelExceptionConditions = jest
        .fn()
        .mockResolvedValue(updatedContractTermCondition);
    const mockContractTerm = {
        data: contractTerm,
        error: undefined,
        loading: false,
    };
    const render = () => renderInAppContext(<ContractTerms />);

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        jest.spyOn(
            contractTermMutation,
            'useUpdateContractTerm'
        ).mockReturnValue(updateBaseTerm);
        jest.spyOn(
            contractTermConditionMutation,
            'useUpdateContractTermConditions'
        ).mockReturnValue(updateLabelExceptionConditions);
        jest.spyOn(storesQuery, 'useStoreList').mockReturnValue({
            data: storeList as GetStoresQuery,
            loading: false,
        });
        jest.spyOn(transactionTypeQuery, 'useTransactionTypes').mockReturnValue(
            {
                data: transactionTypeList as GetTransactionTypesQuery,
                loading: false,
            }
        );
    });

    it('renders Label <ContractTerms>', () => {
        render();
        expect(screen.getAllByText('Label')).toBeDefined();

        const saveButton = screen.getByRole('button', { name: 'Save' });
        expect(saveButton).toBeDefined();

        const cancelButton = screen.getByRole('button', { name: 'Cancel' });
        expect(cancelButton).toBeDefined();
    });

    it('throws an error when tries save label base term by removing existing labels', async () => {
        jest.spyOn(
            contractTermValidation,
            'contractTermFormValidation'
        ).mockReturnValue([]);
        jest.spyOn(
            contractTermValidation,
            'areBaseTermLabelsRemoved'
        ).mockReturnValue(true);
        jest.spyOn(contractTermQuery, 'useContractTerm').mockReturnValue(
            mockContractTerm
        );
        const errorMessage =
            'Label has product and/or track exceptions on this contract. ' +
            'You cannot remove the label in use from the base term.';

        render();
        const saveButton = screen.getByRole('button', { name: 'Save' });
        fireEvent.click(saveButton);

        expect(screen.getByText(errorMessage)).toBeDefined();
        expect(updateBaseTerm).not.toHaveBeenCalled();
    });

    it('saves label base term', async () => {
        jest.spyOn(
            contractTermValidation,
            'contractTermFormValidation'
        ).mockReturnValue([]);
        jest.spyOn(
            contractTermValidation,
            'areBaseTermLabelsRemoved'
        ).mockReturnValue(false);
        jest.spyOn(contractTermQuery, 'useContractTerm').mockReturnValue(
            mockContractTerm
        );

        render();
        const saveButton = screen.getByRole('button', { name: 'Save' });
        fireEvent.click(saveButton);

        expect(updateBaseTerm).toHaveBeenCalledWith({
            variables: {
                attachments: ['32486'],
                contractId: '123',
                contractTermId: '1',
                isBaseTerm: true,
                termType: 'label',
            },
        });
    });

    it('renders error message', async () => {
        jest.spyOn(
            contractTermValidation,
            'contractTermFormValidation'
        ).mockReturnValue([]);
        jest.spyOn(
            contractTermValidation,
            'areBaseTermLabelsRemoved'
        ).mockReturnValue(false);
        jest.spyOn(contractTermQuery, 'useContractTerm').mockReturnValue(
            mockContractTerm
        );

        const updateContractTerm = jest
            .fn()
            .mockRejectedValue([{ extensions: { response: { status: 500 } } }]);
        jest.spyOn(
            contractTermMutation,
            'useUpdateContractTerm'
        ).mockReturnValue(updateContractTerm);

        render();
        const saveButton = screen.getByRole('button', { name: 'Save' });
        fireEvent.click(saveButton);
        await waitFor(() => {
            expect(screen.getByText(CONTRACT_TERM_ERROR_MSG)).toBeDefined();
        });
    });
});
