import { act, renderHook } from '@testing-library/react';
import { useAccountChange } from '../use-account-change';
import type { TransferRow } from 'src/types/abacus-contract-earnings-transfer';
import type { RowUiState } from 'src/components/contract-earnings-transfer/contract-earnings-transfer-config-screen-columns';

const mockQueryResult = {
    data: {
        abacusContracts: {
            items: [
                { contractId: 101, contractName: 'Contract Alpha' },
                { contractId: 102, contractName: 'Contract Beta' },
            ],
        },
    },
};

const mockQuery = jest.fn().mockResolvedValue(mockQueryResult);

jest.mock('@apollo/client', () => ({
    ...jest.requireActual('@apollo/client'),
    useApolloClient: () => ({ query: mockQuery }),
}));

describe('useAccountChange', () => {
    const selectedContractIds = ['102'];
    let onRowChange: jest.Mock<void, [number, Partial<TransferRow>]>;
    let updateRowUiState: jest.Mock<void, [number, Partial<RowUiState>]>;

    beforeEach(() => {
        onRowChange = jest.fn();
        updateRowUiState = jest.fn();
        mockQuery.mockResolvedValue(mockQueryResult);
    });

    afterEach(jest.restoreAllMocks);

    const renderAccountChange = () =>
        renderHook(() =>
            useAccountChange({
                selectedContractIds,
                onRowChange,
                updateRowUiState,
            })
        );

    it('clears row and ui state when accountId is undefined', async () => {
        const { result } = renderAccountChange();

        await act(async () => {
            await result.current(0, undefined, undefined);
        });

        expect(updateRowUiState).toHaveBeenCalledWith(0, {
            selectedAccount: undefined,
            selectedContract: undefined,
            contractOptions: [],
            contractsLoading: false,
        });
        expect(onRowChange).toHaveBeenCalledWith(0, {
            toAccountId: undefined,
            toContractId: undefined,
        });
        expect(mockQuery).not.toHaveBeenCalled();
    });

    it('sets contractsLoading and clears contract when accountId is provided', async () => {
        const { result } = renderAccountChange();
        const account = { label: 'My Account', value: '42' };

        await act(async () => {
            await result.current(0, '42', account);
        });

        expect(updateRowUiState).toHaveBeenNthCalledWith(1, 0, {
            selectedAccount: account,
            selectedContract: undefined,
            contractOptions: [],
            contractsLoading: true,
        });
        expect(onRowChange).toHaveBeenCalledWith(0, {
            toAccountId: '42',
            toContractId: undefined,
        });
    });

    it('fetches contracts for the account and populates options', async () => {
        const { result } = renderAccountChange();
        const account = { label: 'My Account', value: '42' };

        await act(async () => {
            await result.current(0, '42', account);
        });

        expect(mockQuery).toHaveBeenCalledWith(
            expect.objectContaining({
                variables: { accountIds: [42] },
            })
        );

        expect(updateRowUiState).toHaveBeenLastCalledWith(0, {
            contractOptions: [
                { label: 'Contract Alpha', value: '101', disabled: false },
                { label: 'Contract Beta', value: '102', disabled: true },
            ],
            contractsLoading: false,
        });
    });

    it('sets empty contractOptions when no contracts returned', async () => {
        mockQuery.mockResolvedValueOnce({
            data: { abacusContracts: { items: [] } },
        });

        const { result } = renderAccountChange();

        await act(async () => {
            await result.current(1, '99', {
                label: 'Empty Account',
                value: '99',
            });
        });

        expect(updateRowUiState).toHaveBeenLastCalledWith(1, {
            contractOptions: [],
            contractsLoading: false,
        });
    });

    it('updates the correct row index', async () => {
        const { result } = renderAccountChange();
        const account = { label: 'Account 3', value: '30' };

        await act(async () => {
            await result.current(3, '30', account);
        });

        expect(updateRowUiState).toHaveBeenNthCalledWith(
            1,
            3,
            expect.any(Object)
        );
        expect(onRowChange).toHaveBeenCalledWith(3, expect.any(Object));
        expect(updateRowUiState).toHaveBeenLastCalledWith(
            3,
            expect.any(Object)
        );
    });
});
