import React from 'react';
import { fireEvent, screen, within } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import DragAndDrop from 'src/components/shared/drag-and-drop';
import storeList from 'src/__fixtures__/graphql/store-list-response.json';
import transactionTypeList from 'src/__fixtures__/graphql/transaction-types-response.json';
import * as storesQuery from 'src/apollo/queries/stores';
import * as transactionTypeQuery from 'src/apollo/queries/transaction-types';
import { NrContractTermConditionForm } from 'src/components/contract-terms-form-nr/nr-contract-terms-condition-form';
import {
    DELETE_TERM_CONDITION,
    SET_CONTRACT_TERM_CONDITION,
} from 'src/constants';
import { ContractTermContext } from 'src/contexts/contract-term-context';
import { initialTermState } from 'src/reducers/contract-term/contract-term-reducer';
import type { GetStoresQuery } from 'src/apollo/queries/__generated__/stores';
import type { GetTransactionTypesQuery } from 'src/apollo/queries/transaction-types/__generated__/transaction-types';

const componentInContext = (contextValues: any, props: any) => (
    <ContractTermContext.Provider value={contextValues}>
        <DragAndDrop items={[{ id: props.condition.priority }]}>
            <NrContractTermConditionForm {...props} />
        </DragAndDrop>
    </ContractTermContext.Provider>
);

describe('<NrContractTermConditionForm> tests', () => {
    const render = (contextValues: any, props: any) =>
        renderInAppContext(componentInContext(contextValues, props));

    const condition = {
        contractTermConditionName: null,
        conditions: {
            countries: [],
            stores: [],
            transactionTypes: [],
        },
        contractTermConditionId: null,
        priority: 1,
        commission: '',
    };

    let dispatch: any;
    const dragAndDropCondition = jest.fn();
    const index = 0;
    const props = { condition, dragAndDropCondition, index };

    const state = { ...initialTermState, conditions: [condition] };

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        dispatch = jest.fn();
        jest.spyOn(transactionTypeQuery, 'useTransactionTypes').mockReturnValue(
            {
                data: transactionTypeList as GetTransactionTypesQuery,
                loading: false,
            }
        );
        jest.spyOn(storesQuery, 'useStoreList').mockReturnValue({
            data: storeList as GetStoresQuery,
            loading: false,
        });
    });

    it('renders with null conditions', () => {
        const termCondition = {
            conditions: {
                countries: null,
                stores: null,
                transactionTypes: null,
            },
            contractTermConditionId: null,
            contractTermConditionName: '',
            priority: 1,
            commission: '',
        };
        const termState = { ...initialTermState, conditions: [termCondition] };
        const contextValues = { state: termState, dispatch };
        const { container } = render(contextValues, props);
        expect(container).toBeDefined();
    });

    it('is not draggable when contract term condition is the only condition', () => {
        const contextValues = { state, dispatch };
        render(contextValues, props);

        expect(
            screen.getByTestId('nrContractTermConditionDragIcon')
        ).not.toContain('class="Glyph Drag');
    });

    it('is draggable when contract term condition is one of many conditions', () => {
        const draggableProps = { ...props, isDraggable: true };
        const contextValues = { state, dispatch };
        render(contextValues, draggableProps);

        expect(screen.getByTestId('DragGlyphIcon')).toBeDefined();
    });

    it('is not deletable when contract term condition is the only condition', () => {
        const contextValues = { state, dispatch };
        render(contextValues, props);

        expect(
            screen.queryByTestId('contractTermConditionDeleteIcon')
        ).toBeNull();
    });

    it('dispatches condition update when transaction types is selected', async () => {
        const stores = storeList.deliveryStoresV2.items.map(({ id, name }) => ({
            label: name,
            value: `${id}`,
        }));
        const transactionTypes = transactionTypeList?.transactionTypes.map(
            ({ txnTypeCode, txnTypeName, txnTypeId }) => ({
                label: txnTypeCode.concat(' - ', txnTypeName),
                value: txnTypeId,
            })
        );
        const termState = { ...initialTermState, stores, transactionTypes };
        const contextValues = { state: termState, dispatch };
        render(contextValues, props);

        const selector = screen.getByTestId('TransactionTypeMultiSelect');
        fireEvent.click(
            within(selector).getByRole('combobox', { hidden: true })
        );
        const txnTypeSelect = screen.getByTestId('SuiteListViewFilterInput');
        expect(txnTypeSelect).toBeDefined();

        const txnTypeCode =
            transactionTypeList['transactionTypes'][0]['txnTypeCode'];
        const txnTypeName =
            transactionTypeList['transactionTypes'][0]['txnTypeName'];
        const txnTypeId =
            transactionTypeList['transactionTypes'][0]['txnTypeId'];
        const txnTypeDisplayName = `${txnTypeCode} - ${txnTypeName}`;

        expect(screen.getByText(txnTypeDisplayName)).toBeDefined();
        const option = screen
            .getByText(txnTypeDisplayName, {
                selector: '.SuiteListView .SuiteListView-option-label',
            })
            .closest('.SuiteListView-option');
        if (option) fireEvent.click(option);

        expect(dispatch).toHaveBeenCalledWith({
            type: SET_CONTRACT_TERM_CONDITION,
            index,
            name: 'transactionTypes',
            value: [txnTypeId],
        });
    });

    it('dispatches condition update when store is selected', async () => {
        const stores = storeList.deliveryStoresV2.items.map(({ id, name }) => ({
            label: name,
            value: `${id}`,
        }));
        const transactionTypes = transactionTypeList?.transactionTypes.map(
            ({ txnTypeCode, txnTypeName, txnTypeId }) => ({
                label: txnTypeCode.concat(' - ', txnTypeName),
                value: txnTypeId,
            })
        );
        const termState = { ...initialTermState, stores, transactionTypes };
        const contextValues = { state: termState, dispatch };
        render(contextValues, props);

        const selector = screen.getByTestId('ServiceMultiSelect');
        fireEvent.click(
            within(selector).getByRole('combobox', { hidden: true })
        );
        const storeSelect = within(selector).getByTestId(
            'SuiteListViewFilterInput'
        );
        expect(storeSelect).toBeDefined();

        const storeItems = storeList['deliveryStoresV2']['items'];
        const storeId = storeItems[0]['id'];
        const storeName = storeItems[0]['name'];

        const option = screen
            .getByText(storeName, {
                selector: '.SuiteListView .SuiteListView-option-label',
            })
            .closest('.SuiteListView-option');

        if (option) fireEvent.click(option);

        expect(dispatch).toHaveBeenCalledWith({
            type: SET_CONTRACT_TERM_CONDITION,
            index,
            name: 'stores',
            value: [storeId.toString()],
        });
    });

    it('dispatches condition update when country is selected', async () => {
        const contextValues = { state, dispatch };
        render(contextValues, props);

        const selector = screen.getByTestId('SuiteMarketSelector');
        fireEvent.click(
            within(selector).getByRole('combobox', { hidden: true })
        );
        const countrySelect = within(selector).getByTestId(
            'SuiteListViewFilterInput'
        );
        expect(countrySelect).toBeDefined();

        const option = screen
            .getByText('United States', {
                selector: '.SuiteListView .SuiteListView-option-label',
            })
            .closest('.SuiteListView-option')!;
        fireEvent.click(option);

        expect(dispatch).toHaveBeenCalledWith({
            type: SET_CONTRACT_TERM_CONDITION,
            index,
            name: 'countries',
            value: ['USA'],
        });
    });

    it('dispatches condition update when commission is changed', () => {
        const contextValues = { state, dispatch };
        render(contextValues, props);

        const commission = '55.55';
        const commissionInput = screen.getByTestId('commission');
        fireEvent.change(commissionInput, { target: { value: commission } });

        expect(dispatch).toHaveBeenCalledWith({
            type: SET_CONTRACT_TERM_CONDITION,
            index,
            name: 'commission',
            value: commission,
        });
    });

    it('dispatches DELETE_TERM_CONDITION on click of delete icon', () => {
        const termState = {
            ...initialTermState,
            conditions: [condition, condition],
        };

        const contextValues = { state: termState, dispatch };
        render(contextValues, props);
        const deleteButtons = screen.getAllByTestId(
            'nrContractTermConditionDeleteIcon'
        );
        fireEvent.click(deleteButtons[0]);

        expect(dispatch).toHaveBeenCalledWith({
            type: DELETE_TERM_CONDITION,
            termCondition: condition,
            termConditionIndex: 0,
        });
    });
});
