import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { contractDetails as contractDetailResponse } from 'src/__fixtures__/graphql/contract-details';
import { createContractTermResponse } from 'src/__fixtures__/graphql/contract-term';
import * as productSearchResponse from 'src/__fixtures__/graphql/product-search-response.json';
import * as storeList from 'src/__fixtures__/graphql/store-list-response.json';
import * as transactionTypeList from 'src/__fixtures__/graphql/transaction-types-response.json';
import * as contractTermMutation from 'src/apollo/mutations/contract-term';
import * as contractTermConditionMutation from 'src/apollo/mutations/contract-term-condition';
import * as contractQuery from 'src/apollo/queries/contract';
import * as productQuery from 'src/apollo/queries/product-search';
import * as storesQuery from 'src/apollo/queries/stores';
import * as transactionTypeQuery from 'src/apollo/queries/transaction-types';
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 { GetContractQuery } from 'src/apollo/queries/contract/__generated__/get-contract';
import type { SearchProductsAbacusQuery } from 'src/apollo/queries/product-search/__generated__/get-products';
import type { GetTransactionTypesQuery } from 'src/apollo/queries/transaction-types/__generated__/transaction-types';

type SearchProducts = NonNullable<SearchProductsAbacusQuery['searchProducts']>;
type Product = NonNullable<SearchProducts['products'][0]>;

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

describe('<ContractTerms> product exception tests', () => {
    const addContractTerm = jest.fn().mockResolvedValue({
        data: createContractTermResponse,
        error: undefined,
        loading: false,
    });
    const addContractTermConditions = jest.fn().mockResolvedValue([]);

    const render = () => renderInAppContext(<ContractTerms />);

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        jest.spyOn(
            contractTermMutation,
            'useCreateContractTerm'
        ).mockReturnValue(addContractTerm);
        jest.spyOn(
            contractTermConditionMutation,
            'useCreateContractTermConditions'
        ).mockReturnValue(addContractTermConditions);
        jest.spyOn(transactionTypeQuery, 'useTransactionTypes').mockReturnValue(
            {
                data: transactionTypeList as GetTransactionTypesQuery,
                loading: false,
            }
        );
        jest.spyOn(storesQuery, 'useStoreList').mockReturnValue({
            data: storeList as GetStoresQuery,
            loading: false,
        });
        jest.spyOn(contractQuery, 'useContract').mockReturnValue({
            data: {
                abacusContract: contractDetailResponse.abacusContract,
            } as GetContractQuery,
            error: undefined,
            loading: false,
        });
        jest.spyOn(productQuery, 'useProductSearch').mockReturnValue(
            async () =>
                await Promise.resolve(
                    productSearchResponse.searchProducts.products as Product[]
                )
        );
    });

    it('renders <ContractTerms>', () => {
        render();
        expect(screen.getByText('Product Exception')).toBeDefined();

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

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

    it('displays validation error when present', async () => {
        const formValidationSpy = jest
            .spyOn(contractTermValidation, 'contractTermFormValidation')
            .mockReturnValue(['Say please']);
        render();

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

        await waitFor(() => {
            expect(formValidationSpy).toHaveBeenCalled();
        });

        expect(screen.getByText('Say please')).toBeDefined();
    });

    it('saves contract term exception and conditions', async () => {
        jest.spyOn(
            contractTermValidation,
            'contractTermFormValidation'
        ).mockReturnValue([]);
        jest.spyOn(
            contractTermValidation,
            'areBaseTermLabelsRemoved'
        ).mockReturnValue(false);
        render();
        const productSelect = screen.getByRole('combobox');
        fireEvent.change(productSelect, {
            target: { value: 'Test Product 1' },
        });
        await screen.findByText('Test Product 1-193483764306');
        fireEvent.keyDown(productSelect, {
            key: 'Enter',
            code: 'Enter',
            charCode: 13,
        });

        const termRate = await screen.findByTestId('termRate');
        fireEvent.change(termRate, { target: { value: '1.3' } });
        const saveButton = screen.getByRole('button', { name: 'Save' });
        fireEvent.click(saveButton);

        await waitFor(() => {
            expect(addContractTerm).toHaveBeenCalledWith({
                variables: {
                    attachments: ['193483764306'],
                    attachmentsRelations: { labelIds: ['32486'] },
                    contractId: '123',
                    contractTermId: null,
                    isBaseTerm: false,
                    termType: 'product',
                },
            });
            expect(addContractTermConditions).toHaveBeenCalled();
        });
    });
});
