import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { Identity } from '@theorchard/suite-frontend';
import { renderInAppContext } from '@theorchard/suite-testing';
import { Route } from 'react-router-dom';
import { contractDetails as contract } from 'src/__fixtures__/graphql/contract-details';
import { AbacusContractLifecycleStatus } from 'src/apollo/definitions/globalTypes';
import * as contractMutations from 'src/apollo/mutations/contract';
import { updateContractMessage } from 'src/apollo/type-constants/contract';
import {
    ContractLifecycleEditGeneralContractInformation,
    ContractLifecycleEditGeneralContractInformationPropsTypes,
} from 'src/components/contract-lifecycle-edit/contract-lifecycle-edit-general-contract-information';
import { getContractDetail } from 'src/urls/frontend-royalties';
import { mockUseRunControllerList } from '../../../__fixtures__/graphql/run-controller-list-hooks';
import * as runControllers from '../../../__fixtures__/graphql/run-controller-list-response.json';
import {
    CONTRACT_TYPES,
    UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION,
    USER_FEATURES,
} from '../../../constants';

const contractId = contract.abacusContract.contractId;

const render = (
    props: ContractLifecycleEditGeneralContractInformationPropsTypes,
    mockIdentity: Partial<Identity> = {}
) =>
    renderInAppContext(
        <Route path="/contract/:contractId">
            <ContractLifecycleEditGeneralContractInformation {...props} />
        </Route>,
        {
            pathname: getContractDetail(contractId),
            identity: mockIdentity as Identity,
        }
    );
const updateContract = jest.fn().mockResolvedValue({
    data: { abacusUpdateContract: contract.abacusContract },
    loading: false,
});

const props: ContractLifecycleEditGeneralContractInformationPropsTypes = {
    accountName: 'Test Account',
    isSidecarOpen: true,
    onRequestCloseSidecar: jest.fn(),
    contractGeneralInfo: {
        accountId: '123',
        contractType: CONTRACT_TYPES.DISTRIBUTION,
        contractName: 'Test contract',
        executionDate: '2024-09-10',
        runControllerId: '1',
        initialStartDate: '2023-02-20',
        runControllerName: 'Test runcontroller name',
        isExcludedFromAccountingRun: false,
        isPaythroughContract: false,
        isPrimaryContract: false,
        contracts: [],
    },
};

describe('<ContractLifecycleEditGeneralContractInformation />', () => {
    beforeEach(() =>
        jest
            .spyOn(contractMutations, 'useUpdateContract')
            .mockReturnValue(updateContract)
    );
    afterEach(jest.restoreAllMocks);

    it('renders sidecar with contract edit form', () => {
        render(props);

        expect(screen.getByTestId('EditContractInfoSidecar')).toBeVisible();
    });

    it('renders contract edit form (edit run controller FF off)', () => {
        render(props);

        expect(screen.getByText('General Information')).toBeVisible();
        expect(screen.getByTestId('contractNameInput')).toHaveValue(
            props.contractGeneralInfo.contractName
        );
        expect(screen.getByText('Sep 10, 2024')).toBeVisible();
        expect(screen.getByText('Test runcontroller name')).toBeVisible();
        expect(
            screen.queryByTestId('runControllerSelect')
        ).not.toBeInTheDocument();

        const switches = screen.getAllByTestId('FormSwitch');
        expect(switches[0]).toHaveAttribute('checked');
    });

    it('disables the save button if the contract name input is blank string', () => {
        render(props);

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

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

    it('saves changes when the Save button is clicked', () => {
        render(props);

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

        expect(updateContract).toHaveBeenCalledWith({
            variables: {
                contractId,
                contractName: 'Test contract',
                executionDate: '2024-09-10',
                initialStartDate: '2023-02-20',
                isExcludedFromAccountingRun: false,
                isPaythroughContract: false,
                isPrimaryContract: false,
                runControllerId: '1',
            },
        });
    });

    it('renders a success toast popup on updating contract successfully', async () => {
        render(props);
        const saveButton = screen.getByRole('button', { name: 'save' });
        fireEvent.click(saveButton);

        const toast = await screen.findByTestId('Toast');
        expect(toast).toBeVisible();
        expect(toast).toHaveTextContent(updateContractMessage('Test contract'));
    });

    it('closes sidecar when isSidecarOpen is false', () => {
        render({ ...props, isSidecarOpen: false });

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

    describe('changing the run controller (FF on)', () => {
        const { runControllerName, runControllerId } =
            runControllers.abacusRunControllers.items[0];
        const { runControllerName: runControllerName2 } =
            runControllers.abacusRunControllers.items[1];
        const mockIdentity = {
            features: {
                [USER_FEATURES.ABACUS_CONTRACT_EDIT_RUN_CONTROLLER]: true,
            },
        };

        beforeEach(() => {
            mockUseRunControllerList();
            render(
                {
                    ...props,
                    contractGeneralInfo: {
                        ...props.contractGeneralInfo,
                        runControllerId,
                        runControllerName,
                    },
                },
                mockIdentity
            );
        });

        it('renders the run controller select', () => {
            expect(screen.getByTestId('runControllerSelect')).toBeVisible();
        });

        it('displays a modal warning when run controller is changed and user clicks save', async () => {
            const user = userEvent.setup();
            fireEvent.keyDown(screen.getByRole('combobox'), {
                key: 'ArrowDown',
            });
            await user.click(screen.getByText(runControllerName2));

            const runControllerOption = screen.getByText(runControllerName2);
            fireEvent.click(runControllerOption);

            expect(
                screen.queryByText(UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION)
            ).not.toBeInTheDocument();

            await user.click(screen.getByRole('button', { name: 'save' }));

            expect(
                screen.getByText(UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION)
            ).toBeVisible();
        });

        it('does not displays a modal warning when run controller is not changed and user clicks save', async () => {
            const user = userEvent.setup();
            await user.click(screen.getByRole('button', { name: 'save' }));

            expect(
                screen.queryByText(UPDATE_RUN_CONTROLLER_ALERT_EXPLANATION)
            ).not.toBeInTheDocument();
        });
    });

    describe('Flowthrough contract field', () => {
        it('saves isPaythroughContract as true when the switch is toggled on', () => {
            render(props);

            const switches = screen.getAllByTestId('FormSwitch');
            const paythroughSwitch = switches[1]; // Second switch is paythrough contract
            fireEvent.click(paythroughSwitch);

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

            expect(updateContract).toHaveBeenCalledWith({
                variables: {
                    contractId,
                    contractName: 'Test contract',
                    executionDate: '2024-09-10',
                    initialStartDate: '2023-02-20',
                    isExcludedFromAccountingRun: false,
                    isPaythroughContract: true,
                    isPrimaryContract: false,
                    runControllerId: '1',
                },
            });
        });

        it('saves isPaythroughContract as false when the switch is toggled off', () => {
            const updatedProps = {
                ...props,
                contractGeneralInfo: {
                    ...props.contractGeneralInfo,
                    isPaythroughContract: true,
                },
            };
            render(updatedProps);

            const switches = screen.getAllByTestId('FormSwitch');
            const paythroughSwitch = switches[1]; // Second switch is paythrough contract
            fireEvent.click(paythroughSwitch);

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

            expect(updateContract).toHaveBeenCalledWith({
                variables: {
                    contractId,
                    contractName: 'Test contract',
                    executionDate: '2024-09-10',
                    initialStartDate: '2023-02-20',
                    isExcludedFromAccountingRun: false,
                    isPaythroughContract: false,
                    isPrimaryContract: false,
                    runControllerId: '1',
                },
            });
        });
    });

    describe('Primary contract field', () => {
        const mockIdentity = {
            features: {
                [USER_FEATURES.ABACUS_PRIMARY_CONTRACT]: true,
            },
        };
        it('saves isPrimaryContract as true when the switch is toggled on', () => {
            render(props, mockIdentity);

            const switches = screen.getAllByTestId('FormSwitch');
            const primaryContractSwitch = switches[2];
            fireEvent.click(primaryContractSwitch);

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

            expect(updateContract).toHaveBeenCalledWith({
                variables: {
                    contractId,
                    contractName: 'Test contract',
                    executionDate: '2024-09-10',
                    initialStartDate: '2023-02-20',
                    isExcludedFromAccountingRun: false,
                    isPaythroughContract: false,
                    isPrimaryContract: true,
                    runControllerId: '1',
                },
            });
        });

        it('saves isPrimaryContract as false when the switch is toggled off', () => {
            const updatedProps = {
                ...props,
                contractGeneralInfo: {
                    ...props.contractGeneralInfo,
                    contracts: [
                        {
                            contractId,
                            contractType: CONTRACT_TYPES.DISTRIBUTION,
                            contractName: 'Test contract',
                            isPrimaryContract: true,
                            lifecycle: {
                                lifecycleStatus:
                                    AbacusContractLifecycleStatus.ACTIVE,
                            },
                        },
                    ],
                    isPrimaryContract: true,
                },
            };
            render(updatedProps, mockIdentity);

            const switches = screen.getAllByTestId('FormSwitch');
            const primaryContractSwitch = switches[2];
            fireEvent.click(primaryContractSwitch);

            const warningText = screen.getByText('Remove Contract as Primary');
            expect(warningText).toBeDefined();

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

            expect(updateContract).toHaveBeenCalledWith({
                variables: {
                    contractId,
                    contractName: 'Test contract',
                    executionDate: '2024-09-10',
                    initialStartDate: '2023-02-20',
                    isExcludedFromAccountingRun: false,
                    isPaythroughContract: false,
                    isPrimaryContract: false,
                    runControllerId: '1',
                },
            });
        });

        it('displays a modal warning when non primary contract is updated to primary and user clicks save', () => {
            const updatedProps = {
                ...props,
                contractGeneralInfo: {
                    ...props.contractGeneralInfo,
                    contracts: [
                        {
                            contractId: 123,
                            contractType: CONTRACT_TYPES.DISTRIBUTION,
                            contractName: 'Test contract',
                            isPrimaryContract: true,
                            lifecycle: {
                                lifecycleStatus:
                                    AbacusContractLifecycleStatus.ACTIVE,
                            },
                        },
                    ],
                    isPrimaryContract: false,
                },
            };
            render(updatedProps, mockIdentity);

            const switches = screen.getAllByTestId('FormSwitch');
            const primaryContractSwitch = switches[2];
            fireEvent.click(primaryContractSwitch);

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

            expect(
                screen.getByText('Set New Contract as Primary')
            ).toBeDefined();
        });
    });
});
