import React from 'react';
import { fireEvent, screen, within } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { Identity } from '@theorchard/suite-frontend';
import { renderInAppContext } from '@theorchard/suite-testing';
import { TOOLTIP_EXECUTION_DATE } from 'src/apollo/type-constants/contract';
import {
    ContractLifecycleEditContractFrom,
    ContractLifecycleEditContractFromPropsTypes,
} from 'src/components/contract-lifecycle-edit/contract-lifecycle-edit-contract-form';
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_MESSAGE,
    USER_FEATURES,
} from '../../../constants';

describe('<ContractLifecycleEditContractFrom />', () => {
    const render = (
        props: ContractLifecycleEditContractFromPropsTypes,
        mockIdentity: Partial<Identity> = {}
    ) =>
        renderInAppContext(<ContractLifecycleEditContractFrom {...props} />, {
            identity: mockIdentity as Identity,
        });

    afterEach(jest.restoreAllMocks);

    const props: ContractLifecycleEditContractFromPropsTypes = {
        contract: {
            accountId: '123',
            contractType: 'test',
            contractName: 'Test contract',
            executionDate: '2024-09-10',
            initialStartDate: '2023-02-20',
            runControllerId: '1',
            runControllerName: 'Test runcontroller name',
            isExcludedFromAccountingRun: false,
            isPaythroughContract: false,
        },
        disableSaveButton: jest.fn(),
        errorMsg: null,
        handleFormChange: jest.fn(),
    };

    it('renders each of the form labels', () => {
        render(props);

        expect(screen.getByText('Contract Name')).toBeDefined();
        expect(screen.getByText('Execution Date')).toBeDefined();
        expect(screen.getByText('Initial Period Start Date')).toBeDefined();
        expect(screen.getByText('Run Controller')).toBeDefined();
        expect(screen.getByText('Include in the Run')).toBeDefined();
        expect(screen.getByText('Flowthrough Contract')).toBeDefined();
    });

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

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

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

    it('calls handleFormChange on click of switch component', () => {
        render(props);

        const switches = screen.getAllByTestId('FormSwitch');
        const contractIncludedInRun = switches[0];
        fireEvent.click(contractIncludedInRun);
        expect(props.handleFormChange).toHaveBeenCalledWith(
            'isExcludedFromAccountingRun',
            true
        );
    });

    it('calls handleFormChange when the contract name input changes', () => {
        render(props);

        const contractNameInput = screen.getByTestId('contractNameInput');
        fireEvent.change(contractNameInput, { target: { value: 'Test' } });
        expect(props.handleFormChange).toHaveBeenCalledWith(
            'contractName',
            'Test'
        );
    });

    it('toggles off switch component when isExcludedFromAccountingRun is true', () => {
        props.contract.isExcludedFromAccountingRun = true;
        render(props);

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

    it('calls handleFormChange when execution date is selected', async () => {
        render(props);

        const executionDate = await screen.findByTestId('executionDate');
        fireEvent.click(within(executionDate).getAllByRole('button')[1]);

        const dateInput = await within(executionDate).findByRole('textbox');
        fireEvent.change(dateInput, { target: { value: '2024-06-20' } });

        expect(props.handleFormChange).toHaveBeenCalledWith(
            'executionDate',
            '2024-06-20'
        );
    });

    it('shows tooltip on hover for the execution date', async () => {
        render(props);

        const executionDateTooltip = screen.getByTestId('HelpTooltip');
        fireEvent.mouseEnter(executionDateTooltip);
        const tooltipText = await screen.findByTestId('HelpTooltipOverlay');
        expect(tooltipText).toBeInTheDocument();
        expect(tooltipText).toHaveTextContent(TOOLTIP_EXECUTION_DATE);
    });

    it('calls handleFormChange when initial start date is selected', async () => {
        render(props);

        const initialStartDate = await screen.findByTestId('initialStartDate');
        fireEvent.click(within(initialStartDate).getAllByRole('button')[1]);

        const dateInput = await within(initialStartDate).findByRole('textbox');
        fireEvent.change(dateInput, { target: { value: '2023-02-21' } });

        expect(props.handleFormChange).toHaveBeenCalledWith(
            'initialStartDate',
            '2023-02-21'
        );
    });

    it('renders an error message', async () => {
        props.errorMsg = 'Contract Error Message.';
        render(props);

        expect(screen.getByText(props.errorMsg)).toBeDefined();
    });

    it('hides primary contract switch when ff is disabled and contract is distribution', () => {
        const updatedProps = {
            ...props,
            contract: {
                ...props.contract,
                contractType: CONTRACT_TYPES.DISTRIBUTION,
                isPrimaryContract: false,
            },
        };
        const mockIdentityDisabledFeature = {
            features: {
                [USER_FEATURES.ABACUS_PRIMARY_CONTRACT]: false,
            },
        };
        render(updatedProps, mockIdentityDisabledFeature);

        expect(screen.queryByText('Is Primary Contract')).toBeNull();
    });

    describe('run controller dropdown', () => {
        const user = userEvent.setup();
        const { runControllerName: runControllerName2 } =
            runControllers.abacusRunControllers.items[1];
        const { runControllerName, runControllerId } =
            runControllers.abacusRunControllers.items[0];
        const mockIdentity = {
            features: {
                [USER_FEATURES.ABACUS_CONTRACT_EDIT_RUN_CONTROLLER]: true,
            },
        };

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

        it("displays contract's current run controller name in the select", async () => {
            expect(screen.getByTestId('runControllerSelect')).toHaveTextContent(
                runControllerName
            );
        });

        it("clicking on a run controller option that is not the contract's current run controller shows an Alert", async () => {
            expect(
                screen.queryByText(UPDATE_RUN_CONTROLLER_ALERT_MESSAGE)
            ).not.toBeInTheDocument();

            fireEvent.keyDown(screen.getByRole('combobox'), {
                key: 'ArrowDown',
            });
            await user.click(screen.getByText(runControllerName2));

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

        it('clicking on a different run controller then going back to the original hides the Alert', async () => {
            fireEvent.keyDown(screen.getByRole('combobox'), {
                key: 'ArrowDown',
            });
            await user.click(screen.getByText(runControllerName2));

            expect(
                screen.getByText(UPDATE_RUN_CONTROLLER_ALERT_MESSAGE)
            ).toBeVisible();

            fireEvent.keyDown(screen.getByRole('combobox'), {
                key: 'ArrowDown',
            });
            await user.click(
                screen.getByRole('option', { name: runControllerName })
            );

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

    describe('paythrough contract switch', () => {
        it('renders paythrough contract switch unchecked when isPaythroughContract is false', () => {
            render(props);

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

        it('renders paythrough contract switch checked when isPaythroughContract is true', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    isPaythroughContract: true,
                },
            };
            render(updatedProps);

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

        it('calls handleFormChange when paythrough contract switch is toggled', () => {
            render(props);

            const switches = screen.getAllByTestId('FormSwitch');
            const paythroughSwitch = switches[1];
            fireEvent.click(paythroughSwitch);

            expect(props.handleFormChange).toHaveBeenCalledWith(
                'isPaythroughContract',
                true
            );
        });

        it('calls handleFormChange with false when paythrough contract switch is toggled off', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    isPaythroughContract: true,
                },
            };
            render(updatedProps);

            const switches = screen.getAllByTestId('FormSwitch');
            const paythroughSwitch = switches[1];
            fireEvent.click(paythroughSwitch);

            expect(props.handleFormChange).toHaveBeenCalledWith(
                'isPaythroughContract',
                false
            );
        });
    });

    describe('primary contract switch when ff is enabled', () => {
        const mockIdentity = {
            features: {
                [USER_FEATURES.ABACUS_PRIMARY_CONTRACT]: true,
            },
        };

        it('hides primary contract switch for KNR contract', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.NEIGHBOURING_RIGHTS,
                    isPrimaryContract: false,
                },
            };
            render(updatedProps, mockIdentity);

            expect(screen.queryByText('Is Primary Contract')).toBeNull();
        });

        it('renders primary contract switch for distribution contract', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.DISTRIBUTION,
                    isPrimaryContract: false,
                },
            };
            render(updatedProps, mockIdentity);

            expect(screen.getByText('Is Primary Contract')).toBeDefined();
        });

        it('renders primary distribution contract switch unchecked when isPrimaryContract is false', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.DISTRIBUTION,
                    isPrimaryContract: false,
                },
            };
            render(updatedProps, mockIdentity);

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

        it('renders primary distribution contract switch checked when isPrimaryContract is true', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.DISTRIBUTION,
                    isPrimaryContract: true,
                },
            };
            render(updatedProps, mockIdentity);

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

        it('calls handleFormChange when primary contract switch is toggled', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.DISTRIBUTION,
                    isPrimaryContract: false,
                },
            };
            render(updatedProps, mockIdentity);

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

            expect(props.handleFormChange).toHaveBeenCalledWith(
                'isPrimaryContract',
                true
            );
        });

        it('calls handleFormChange with false when primary contract switch is toggled off', () => {
            const updatedProps = {
                ...props,
                contract: {
                    ...props.contract,
                    contractType: CONTRACT_TYPES.DISTRIBUTION,
                    isPrimaryContract: true,
                },
            };
            render(updatedProps, mockIdentity);

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

            expect(props.handleFormChange).toHaveBeenCalledWith(
                'isPrimaryContract',
                false
            );
        });
    });
});
