import React from 'react';
import {
    ApolloCache,
    DefaultContext,
    FetchResult,
    MutationFunctionOptions,
} from '@apollo/client';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import {
    AbacusContractMechDeductionAdminType,
    AbacusContractMechDeductionMechanicalType,
    AbacusContractMechDeductionTerritory,
} from 'src/apollo/definitions/globalTypes';
import * as abacusDeleteContractMechanicalDeduction from 'src/apollo/mutations/contract-mechanical-deductions';
import { CONTRACT_MECHANICAL_DEDUCTION_NO_CALCULATION_MSG } from 'src/apollo/type-constants/contract';
import {
    ContractMechanicalDeductionCard,
    ContractMechanicalDeductionCardPropTypes,
} from 'src/components/contract-detail/contract-mechanical-deduction-card';
import type {
    DeleteContractMechanicalDeductionMutation,
    DeleteContractMechanicalDeductionMutationVariables,
} from 'src/apollo/mutations/contract-mechanical-deductions/__generated__/delete-contract-mechanical-deduction';
import { ABACUS_PROFILE } from 'src/constants';

describe('<ContractMechanicalDeductionCard />', () => {
    const identity = createIdentity({ profileType: ABACUS_PROFILE });
    const defaultProps = {
        contractMechanicalDeduction: {
            adminFee: 12.2,
            adminType: AbacusContractMechDeductionAdminType.BOTH,
            contractId: '1234',
            contractMechanicalDeductionId: '1',
            mechanicalType: [AbacusContractMechDeductionMechanicalType.DIGITAL],
            territory: AbacusContractMechDeductionTerritory.USA,
        },
    };

    const render = (props: ContractMechanicalDeductionCardPropTypes) =>
        renderInAppContext(<ContractMechanicalDeductionCard {...props} />, {
            identity,
        });

    describe('expected properties for all <ContractMechanicalDeductionCard />', () => {
        it('renders the territory text', () => {
            render(defaultProps);

            const territorySpan = screen.getByTestId(
                'mechanicalDeductionTerritory'
            );
            expect(territorySpan).toBeDefined();
            expect(territorySpan).toHaveTextContent('US');
        });

        it('renders the mechanical type text for a single type', () => {
            render(defaultProps);

            const mechanicalTypeSpan = screen.getByTestId('mechanicalTypeText');
            expect(mechanicalTypeSpan).toBeDefined();
            expect(mechanicalTypeSpan).toHaveTextContent('Digital downloads');
        });

        it('renders the expected mechanical type text for multiple types and orders them alphabetically', () => {
            const props = {
                ...defaultProps,
                contractMechanicalDeduction: {
                    ...defaultProps.contractMechanicalDeduction,
                    mechanicalType: [
                        AbacusContractMechDeductionMechanicalType.PHYSICAL,
                        AbacusContractMechDeductionMechanicalType.DIGITAL,
                    ],
                },
            };
            render(props);

            const mechanicalTypeSpan = screen.getByTestId('mechanicalTypeText');
            expect(mechanicalTypeSpan).toBeDefined();
            expect(mechanicalTypeSpan).toHaveTextContent(
                'Digital downloads, Physical'
            );
        });

        it('renders the edit button with the expected icon', () => {
            render(defaultProps);
            const editButton = screen.getByTestId('editButton');
            const editIcon = editButton.querySelector('svg');
            expect(editButton).toBeDefined();
            expect(editIcon).toBeDefined();
            expect(editIcon).toHaveClass('EditGlyphIcon');
        });

        it('renders the delete button with the expected icon', () => {
            render(defaultProps);
            const deleteButton = screen.getByTestId('deleteButton');
            const deleteIcon = deleteButton.querySelector('svg');
            expect(deleteButton).toBeDefined();
            expect(deleteIcon).toBeDefined();
            expect(deleteIcon).toHaveClass('TrashGlyphIcon');
        });

        it('renders the admin type', () => {
            render(defaultProps);

            const adminTypeSpan = screen.getByTestId(
                'mechanicalDeductionPaidBy'
            );
            expect(adminTypeSpan).toBeDefined();
            expect(adminTypeSpan).toHaveTextContent('Paid by both');
        });
    });

    describe('when Edit button is clicked', () => {
        it('renders mech deduction edit sidecar on click of edit icon', () => {
            render(defaultProps);
            const editButton = screen.getByTestId('editButton');
            fireEvent.click(editButton);

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

        it('renders edit form', () => {
            render(defaultProps);
            const editButton = screen.getByTestId('editButton');
            fireEvent.click(editButton);

            expect(
                screen.getByText('Mechanical Deduction • US')
            ).toBeInTheDocument();
            expect(screen.getByText('Product Types')).toBeInTheDocument();
            expect(
                screen.getByText('Who Pays For The Deductions?')
            ).toBeInTheDocument();
            expect(screen.getByText('Admin Fee')).toBeInTheDocument();
        });
    });

    describe('when Delete button is clicked', () => {
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        let deleteContractMechanicalDeductionRequestSpy: jest.SpyInstance<
            (
                options?:
                    | MutationFunctionOptions<
                          DeleteContractMechanicalDeductionMutation,
                          DeleteContractMechanicalDeductionMutationVariables,
                          DefaultContext,
                          ApolloCache<any>
                      >
                    | undefined
            ) => Promise<FetchResult<DeleteContractMechanicalDeductionMutation>>
        >;

        const mockDeleteContractMechanicalDeduction = jest
            .fn()
            .mockResolvedValue({
                data: {
                    abacusDeleteContractMechanicalDeduction: {
                        deleted: true,
                    },
                },
            });

        beforeEach(() => {
            deleteContractMechanicalDeductionRequestSpy = jest
                .spyOn(
                    abacusDeleteContractMechanicalDeduction,
                    'useDeleteContractMechanicalDeduction'
                )
                .mockReturnValue(mockDeleteContractMechanicalDeduction);

            render(defaultProps);
            const deleteButton = screen.getByTestId('deleteButton');
            fireEvent.click(deleteButton);
        });

        it('opens the delete modal when the delete button is clicked', () => {
            const modal = screen.getByTestId('deleteModal');
            expect(modal).toBeDefined();
        });

        it('renders the expected title in the delete modal', () => {
            const modal = screen.getByTestId('deleteModal');
            expect(modal).toHaveTextContent('Delete US mechanical deduction?');
        });

        it('renders the expected body text in the delete modal', () => {
            const modal = screen.getByTestId('deleteModal');
            expect(modal).toHaveTextContent(
                `You're about to delete a mechanical deduction for the US territory. This will affect the contract.`
            );
        });

        it('closes the delete modal when the cancel button is clicked', async () => {
            const cancelButton = screen.getByText('No, Cancel');
            fireEvent.click(cancelButton);
            await waitFor(() => {
                const modal = screen.queryByTestId('deleteModal');
                expect(modal).toBeNull();
            });
        });

        it('deletes the mechanical deduction when the "Yes, Delete" button is clicked', async () => {
            const confirmButton = screen.getByText('Yes, Delete');
            fireEvent.click(confirmButton);
            expect(mockDeleteContractMechanicalDeduction).toHaveBeenCalled();
        });

        it('generates a toast message when a mechanical deduction is successfully deleted', async () => {
            const confirmButton = screen.getByText('Yes, Delete');
            fireEvent.click(confirmButton);
            await waitFor(() => {
                const toast = screen.getByTestId('Toast-0');
                expect(toast).toHaveTextContent(
                    'Mechanical deductions have been successfully updated.'
                );
            });
        });
    });

    describe('conditional rendering of the admin fee on the <ContractMechanicalDeductionCard />', () => {
        it('does not render the admin fee if it is null', () => {
            const props = {
                ...defaultProps,
                contractMechanicalDeduction: {
                    ...defaultProps.contractMechanicalDeduction,
                    adminFee: null,
                },
            };
            render(props);

            const adminTypeSpan = screen.getByTestId(
                'mechanicalDeductionPaidBy'
            );
            expect(adminTypeSpan).toBeDefined();
            expect(adminTypeSpan).not.toHaveTextContent('admin fee');
        });

        it('renders the admin fee if it is not null', () => {
            render(defaultProps);

            const adminTypeSpan = screen.getByTestId(
                'mechanicalDeductionPaidBy'
            );
            expect(adminTypeSpan).toBeDefined();
            expect(adminTypeSpan).toHaveTextContent('12.2% admin fee');
        });
    });

    describe('conditional rendering of the warning tooltip on the <ContractMechanicalDeductionCard />', () => {
        it('shows a tooltip for the canada territory', async () => {
            const props = {
                ...defaultProps,
                contractMechanicalDeduction: {
                    ...defaultProps.contractMechanicalDeduction,
                    territory: AbacusContractMechDeductionTerritory.CAN,
                },
            };
            render(props);
            const tooltip = screen.getByTestId('territoryWarningTooltip');
            expect(tooltip).toBeInTheDocument();

            fireEvent.mouseEnter(tooltip);
            expect(
                screen.getByText(
                    CONTRACT_MECHANICAL_DEDUCTION_NO_CALCULATION_MSG
                )
            ).toBeInTheDocument();
        });

        it('shows a tooltip for the rest of the world territory', async () => {
            const props = {
                ...defaultProps,
                contractMechanicalDeduction: {
                    ...defaultProps.contractMechanicalDeduction,
                    territory: AbacusContractMechDeductionTerritory.ROW,
                },
            };
            render(props);
            const tooltip = screen.getByTestId('territoryWarningTooltip');
            expect(tooltip).toBeInTheDocument();

            fireEvent.mouseEnter(tooltip);
            expect(
                screen.getByText(
                    CONTRACT_MECHANICAL_DEDUCTION_NO_CALCULATION_MSG
                )
            ).toBeInTheDocument();
        });

        it('does not show a tooltip for the US territory', async () => {
            render(defaultProps);
            const tooltip = screen.queryByTestId('territoryWarningTooltip');
            expect(tooltip).not.toBeInTheDocument();
        });
    });
});
