import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { contractMechanicalDeductions } from 'src/__fixtures__/graphql/contract-mechanical-deductions';
import * as contractMechDeductionMutations from 'src/apollo/mutations/contract-mechanical-deductions';
import {
    CONTRACT_MECHANICAL_DEDUCTION_PRODUCT_TYPES,
    UPDATE_CONTRACT_MECH_DEDUCTION_MSG,
} from 'src/apollo/type-constants/contract';
import {
    ContractMechanicalDeductionSidecar,
    ContractMechanicalDeductionSidecarPropTypes,
} from 'src/components/contract-mechanical-deduction-edit/contract-mechanical-deduction-sidecar';
import * as mechDeductionFormValidation from 'src/utils/form-validations/contract-mechanical-deduction-form-validation';

describe('<ContractMechanicalDeductionSidecar />', () => {
    const defaultProps: ContractMechanicalDeductionSidecarPropTypes = {
        mechanicalDeduction:
            contractMechanicalDeductions.abacusContractMechanicalDeductions[1],
        isSidecarOpen: true,
        setIsSidecarOpen: jest.fn(),
    };

    let updateContractMechanicalDeduction = jest.fn().mockResolvedValue({});

    const render = (
        props: ContractMechanicalDeductionSidecarPropTypes = defaultProps
    ) => renderInAppContext(<ContractMechanicalDeductionSidecar {...props} />);

    afterEach(jest.restoreAllMocks);

    it('renders the edit contract mechanical deduction form in a sidecar', () => {
        render();

        expect(
            screen.getByTestId('ContractMechanicalDeductionEditSidecar')
        ).toBeInTheDocument();
    });

    it('renders sidecar header', () => {
        render();
        const header = screen.getByTestId(
            'EditContractMechanicalDeductionSidecarHeader'
        );
        expect(header).toHaveTextContent(`Mechanical Deduction • US`);
    });

    it('renders product type "Digital" as checked', () => {
        render();

        const productTypes = screen.getAllByTestId('ProductTypesOptions');
        expect(productTypes[0]).toBeChecked();
        expect(productTypes[1]).not.toBeChecked();
    });

    it('renders admin type "Business" as selected', () => {
        render();

        const adminTypes = screen.getAllByTestId('AdminTypesOptions');
        expect(adminTypes[0]).not.toBeChecked();
        expect(adminTypes[1]).toBeChecked();
        expect(adminTypes[2]).not.toBeChecked();
    });

    it('renders admin fee', () => {
        render();

        const adminFeeOptions = screen.getAllByTestId('AdminFeeOptions');
        expect(adminFeeOptions[0]).not.toBeChecked();
        expect(adminFeeOptions[1]).toBeChecked();

        expect(screen.getByText('Fee')).toBeInTheDocument();
        expect(screen.getByTestId('AdminFeeInput')).toBeInTheDocument();
        expect(screen.getByTestId('AdminFeeInput')).toHaveValue('7');
    });

    it('hides admin fee input box if "No" option is selected', () => {
        render();

        const adminFeeOptions = screen.getAllByTestId('AdminFeeOptions');
        fireEvent.click(adminFeeOptions[0]);

        expect(screen.queryByText('Fee')).not.toBeInTheDocument();
        expect(screen.queryByTestId('AdminFeeInput')).not.toBeInTheDocument();
    });

    it('disables save button if the admin fee input is blank string', () => {
        render();

        const adminFeeInput = screen.getByTestId('AdminFeeInput');
        fireEvent.change(adminFeeInput, { target: { value: '' } });

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

    it('renders an error message if form validation fails', async () => {
        jest.spyOn(
            contractMechDeductionMutations,
            'useUpdateContractMechanicalDeduction'
        ).mockReturnValue(updateContractMechanicalDeduction);
        jest.spyOn(
            mechDeductionFormValidation,
            'contractMechanicalDeductionFormValidation'
        ).mockReturnValue({ adminFee: 'wrong Input' });
        render();

        const saveButton = await screen.findByRole('button', { name: 'save' });
        expect(saveButton).not.toBeDisabled();
        fireEvent.click(saveButton);

        expect(screen.getByText('wrong Input')).toBeInTheDocument();
        expect(updateContractMechanicalDeduction).not.toHaveBeenCalled();
    });

    it('updates a mechanical deduction on click of save button', async () => {
        jest.spyOn(
            contractMechDeductionMutations,
            'useUpdateContractMechanicalDeduction'
        ).mockReturnValue(updateContractMechanicalDeduction);

        render();

        const adminFeeOptions = screen.getAllByTestId('AdminFeeOptions');
        fireEvent.click(adminFeeOptions[0]);

        const productTypes = screen.getAllByTestId('ProductTypesOptions');
        fireEvent.click(productTypes[1]);

        const saveButton = await screen.findByRole('button', { name: 'save' });
        expect(saveButton).not.toBeDisabled();
        fireEvent.click(saveButton);

        expect(updateContractMechanicalDeduction).toHaveBeenCalledWith({
            variables: {
                adminFee: null,
                adminType: defaultProps.mechanicalDeduction.adminType,
                contractMechanicalDeductionId:
                    defaultProps.mechanicalDeduction
                        .contractMechanicalDeductionId,
                mechanicalType: [
                    CONTRACT_MECHANICAL_DEDUCTION_PRODUCT_TYPES.DIGITAL.value,
                    CONTRACT_MECHANICAL_DEDUCTION_PRODUCT_TYPES.PHYSICAL.value,
                ],
            },
        });
        expect(saveButton).toBeDisabled();
    });

    it('renders a success toast popup on updating mech deduction successfully', async () => {
        jest.spyOn(
            contractMechDeductionMutations,
            'useUpdateContractMechanicalDeduction'
        ).mockReturnValue(updateContractMechanicalDeduction);

        render();

        const saveButton = await screen.findByRole('button', { name: 'save' });
        expect(saveButton).not.toBeDisabled();
        fireEvent.click(saveButton);

        expect(updateContractMechanicalDeduction).toHaveBeenCalled();

        const toast = await screen.findByTestId('Toast-0');
        expect(toast).toBeDefined();
        expect(toast).toHaveTextContent(UPDATE_CONTRACT_MECH_DEDUCTION_MSG);
    });

    it('renders an error alert when a mech deduction update fails', async () => {
        updateContractMechanicalDeduction = jest
            .fn()
            .mockRejectedValue(new Error('Server Error'));
        jest.spyOn(
            contractMechDeductionMutations,
            'useUpdateContractMechanicalDeduction'
        ).mockReturnValue(updateContractMechanicalDeduction);

        render();

        const saveButton = await screen.findByRole('button', { name: 'save' });
        expect(saveButton).not.toBeDisabled();
        fireEvent.click(saveButton);

        expect(updateContractMechanicalDeduction).toHaveBeenCalled();

        const alertMessage = await screen.findByTestId(
            'EditContractMechanicalDeductionSidecar-ErrorMsg'
        );
        expect(alertMessage).toHaveTextContent(
            'An error has occurred while updating contract mechanical deduction. ' +
                'Please contact the Accounting Tech Team to resolve this issue.'
        );
    });
});
