import React from 'react';
import {
    fireEvent,
    getByTestId,
    screen,
    waitFor,
} from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { AbacusEarningsTransferTypes } from 'src/apollo/definitions/globalTypes';
import * as createEarningsTransferHook from 'src/hooks/contract-earnings-transfer/create-contract-earnings-transfer';
import * as contractQuery from 'src/apollo/queries/contract';
import { ContractEarningsTransferFullScreenModal } from '../contract-earnings-transfer-fullscreen-modal';

// jsdom does not implement crypto.randomUUID; stub it
Object.defineProperty(global, 'crypto', {
    value: { randomUUID: () => 'test-uuid' },
    configurable: true,
});

const mockHistoryPush = jest.fn();
const mockClose = jest.fn();

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useParams: () => ({ contractId: 'contract-123' }),
    useHistory: () => ({ push: mockHistoryPush }),
}));

jest.mock('@theorchard/suite-components', () => ({
    ...jest.requireActual('@theorchard/suite-components'),
    useFullscreenModal: () => ({ close: mockClose }),
}));

describe('<ContractEarningsTransferFullScreenModal />', () => {
    const history = createMemoryHistory();

    const render = () =>
        renderInAppContext(
            <Router history={history}>
                <ContractEarningsTransferFullScreenModal />
            </Router>
        );

    beforeEach(() => {
        mockHistoryPush.mockClear();
        mockClose.mockClear();

        jest.spyOn(contractQuery, 'useContractById').mockReturnValue({
            data: undefined,
            loading: false,
            error: undefined,
        } as any);
    });

    afterEach(() => {
        jest.clearAllMocks();
    });

    describe('Modal render', () => {
        it('renders the fullscreen modal header', () => {
            render();
            expect(
                screen.getByText(
                    'Set Up Terms for Transfer of Earnings Calculations'
                )
            ).toBeDefined();
        });

        it('renders the Cancel button', () => {
            render();
            expect(
                screen.getByTestId('earningsTransferCancelButton')
            ).toBeDefined();
        });

        it('renders the Create button', () => {
            render();

            const button = screen.getByTestId('earningsTransferSaveButton');
            expect(button).toBeDefined();
            expect(button).toHaveTextContent('Create');
        });

        it('renders the type selection screen initially', () => {
            render();
            expect(screen.getByText('Transfer Type')).toBeDefined();
            expect(
                screen.getByTestId('earningsTransferNextButton')
            ).toBeDefined();
        });

        it('does not render the config screen initially', () => {
            render();
            expect(
                screen.queryByTestId('earningsTransferAddRowButton')
            ).toBeNull();
        });
    });

    describe('Create button state', () => {
        it('is disabled on initial render', () => {
            render();
            const createButton = screen.getByTestId(
                'earningsTransferSaveButton'
            );
            expect(createButton).toBeDisabled();
        });
    });

    describe('Cancel button', () => {
        it('renders confirmation modal on click', async () => {
            render();
            const cancelButton = screen.getByTestId(
                'earningsTransferCancelButton'
            );
            fireEvent.click(cancelButton);

            await waitFor(() => {
                expect(
                    screen.getByText('Are you sure you want to exit?')
                ).toBeDefined();
                expect(screen.getByText('Exit without saving')).toBeDefined();
            });
        });

        it('calls close and navigates to contract detail on cancel', async () => {
            render();
            const cancelButton = screen.getByTestId(
                'earningsTransferCancelButton'
            );
            fireEvent.click(cancelButton);
            fireEvent.click(screen.getByText('Exit without saving'));

            await waitFor(() => {
                expect(mockClose).toHaveBeenCalled();
            });
        });
    });

    describe('Navigation between screens', () => {
        it('shows the config screen after selecting a type and clicking Next', async () => {
            render();

            const selectInput = screen.getByText('Select Transfer Type');
            fireEvent.click(selectInput);
            fireEvent.click(screen.getByText('Transfer of Income'));

            const nextButton = screen.getByTestId('earningsTransferNextButton');
            expect(nextButton).not.toBeDisabled();
            fireEvent.click(nextButton);

            await waitFor(() => {
                expect(
                    screen.getByTestId('earningsTransferAddRowButton')
                ).toBeDefined();
            });
        });

        it('updates the modal title to include the transfer type on config screen', async () => {
            render();

            const selectInput = screen.getByText('Select Transfer Type');
            fireEvent.click(selectInput);
            fireEvent.click(screen.getByText('Override'));

            fireEvent.click(screen.getByTestId('earningsTransferNextButton'));

            await waitFor(() => {
                expect(
                    screen.getByText('Set Up Terms for Override Transfers')
                ).toBeDefined();
            });
        });

        it('navigates back to type screen when Back is clicked', async () => {
            render();

            const selectInput = screen.getByText('Select Transfer Type');
            fireEvent.click(selectInput);
            fireEvent.click(screen.getByText('Balance Transfer'));
            fireEvent.click(screen.getByTestId('earningsTransferNextButton'));

            await waitFor(() => {
                expect(
                    screen.getByTestId('earningsTransferBackButton')
                ).toBeDefined();
            });

            fireEvent.click(screen.getByTestId('earningsTransferBackButton'));
            fireEvent.click(screen.getByText('Change transfer type'));

            await waitFor(() => {
                expect(
                    screen.getByTestId('earningsTransferNextButton')
                ).toBeDefined();
            });
        });
    });

    describe('Save handler', () => {
        it('calls useSaveHandler with the current contractId', () => {
            const useSaveHandlerSpy = jest
                .spyOn(createEarningsTransferHook, 'useCreateHandler')
                .mockReturnValue(jest.fn().mockResolvedValue(undefined));

            render();

            expect(useSaveHandlerSpy).toHaveBeenCalledWith('contract-123');
        });
    });

    describe('isFormValid', () => {
        it('keeps Create button disabled when on config screen with empty row', async () => {
            render();

            const selectInput = screen.getByText('Select Transfer Type');
            fireEvent.click(selectInput);
            fireEvent.click(screen.getByText('Transfer of Income'));
            fireEvent.click(screen.getByTestId('earningsTransferNextButton'));

            await waitFor(() => {
                expect(
                    screen.getByTestId('earningsTransferAddRowButton')
                ).toBeDefined();
            });

            const createButton = screen.getByTestId(
                'earningsTransferSaveButton'
            );
            expect(createButton).toBeDisabled();
        });
    });

    describe('Transfer type label', () => {
        it('shows generic title before type is selected', () => {
            render();
            expect(
                screen.getByText('Set Up Terms for Transfer of Earnings')
            ).toBeDefined();
        });

        it.each([
            [
                AbacusEarningsTransferTypes.TRANSFER,
                'Transfer of Income',
                'Income',
            ],
            [
                AbacusEarningsTransferTypes.RECLASS,
                'Balance Transfer',
                'Balance',
            ],
            [AbacusEarningsTransferTypes.OVERRIDE, 'Override', 'Override'],
        ])(
            'shows correct label for transfer type %s',
            async (_, label, titleLabel) => {
                render();
                const selectInput = screen.getByText('Select Transfer Type');
                fireEvent.click(selectInput);
                fireEvent.click(screen.getByText(label));
                fireEvent.click(
                    screen.getByTestId('earningsTransferNextButton')
                );

                await waitFor(() => {
                    expect(
                        screen.getByText(
                            `Set Up Terms for ${titleLabel} Transfers`
                        )
                    ).toBeDefined();
                });
            }
        );
    });

    describe('Balance Transfers', () => {
        it('defaults and disables some inputs when creating balance transfers', () => {
            render();

            const transferTypeInput = screen.getByText('Select Transfer Type');
            fireEvent.click(transferTypeInput);
            fireEvent.click(screen.getByText('Balance Transfer'));
            fireEvent.click(screen.getByTestId('earningsTransferNextButton'));

            const rateTypeInput = screen.getByTestId(
                'earningsTransferRateType-0'
            );

            expect(rateTypeInput).toHaveTextContent('Percent');
            expect(getByTestId(rateTypeInput, 'SuiteSelectInput')).toHaveClass(
                'disabled'
            );

            const amountInput = screen.getByTestId('earningsTransferAmount-0');

            expect(amountInput).toHaveValue('100');
            expect(amountInput).toBeDisabled();

            const sourceInput = screen.getByTestId('earningsTransferSource-0');

            expect(sourceInput).toHaveTextContent('Closing Balance');
            expect(getByTestId(sourceInput, 'SuiteSelectInput')).toHaveClass(
                'disabled'
            );
        });
    });
});
