import { renderAsync, screen } from '@testing-library/react-native';
import { focusManager } from '@tanstack/react-query';
import { Text, AppState } from 'react-native';

import {
  AppQueryProvider,
  QUERY_CLIENT_DEFAULT_OPTIONS,
  createQueryClient,
  queryClient,
} from '@/core/query';

const mockUseReactQueryDevTools = jest.fn();

jest.mock('@dev-plugins/react-query', () => ({
  useReactQueryDevTools: (...args: unknown[]) =>
    mockUseReactQueryDevTools(...args),
}));

describe('react query infrastructure', () => {
  let addEventListenerSpy: jest.SpyInstance;
  let removeSpy: jest.Mock;
  let setFocusedSpy: jest.SpyInstance;

  beforeEach(() => {
    removeSpy = jest.fn();
    addEventListenerSpy = jest
      .spyOn(AppState, 'addEventListener')
      .mockReturnValue({ remove: removeSpy });
    setFocusedSpy = jest
      .spyOn(focusManager, 'setFocused')
      .mockImplementation(() => undefined);
  });

  afterEach(() => {
    addEventListenerSpy.mockRestore();
    setFocusedSpy.mockRestore();
    jest.clearAllMocks();
  });

  test('creates query clients with the app defaults', () => {
    const client = createQueryClient();
    const defaults = client.getDefaultOptions();

    expect(defaults.queries).toMatchObject(
      QUERY_CLIENT_DEFAULT_OPTIONS.queries,
    );
    expect(defaults.mutations).toMatchObject(
      QUERY_CLIENT_DEFAULT_OPTIONS.mutations,
    );
  });

  test('wraps the app with the shared query client and syncs app focus', async () => {
    await renderAsync(
      <AppQueryProvider>
        <Text>query ready</Text>
      </AppQueryProvider>,
    );

    expect(screen.getByText('query ready')).toBeOnTheScreen();
    expect(mockUseReactQueryDevTools).toHaveBeenCalledWith(queryClient);
    expect(addEventListenerSpy).toHaveBeenCalledWith(
      'change',
      expect.any(Function),
    );

    const listener = addEventListenerSpy.mock.calls[0][1] as (
      status: string,
    ) => void;

    listener('active');
    listener('background');

    expect(setFocusedSpy).toHaveBeenNthCalledWith(1, true);
    expect(setFocusedSpy).toHaveBeenNthCalledWith(2, false);
  });
});
