import React from 'react';

import { Alert } from 'react-native';

import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react-native';
import type { Channel as ChannelType, StreamChat } from 'stream-chat';

import * as AttachmentPickerUtils from '../../../contexts/attachmentPickerContext/AttachmentPickerContext';
import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider';

import { initiateClientWithChannels } from '../../../mock-builders/api/initiateClientWithChannels';

import { AttachmentPickerStore } from '../../../state-store/attachment-picker-store';
import { AttachmentPickerContent } from '../../AttachmentPicker/components/AttachmentPickerContent';
import { AttachmentPickerSelectionBar } from '../../AttachmentPicker/components/AttachmentPickerSelectionBar';
import type { ChannelProps } from '../../Channel/Channel';
import { Channel } from '../../Channel/Channel';
import { Chat } from '../../Chat/Chat';
import { MessageComposer } from '../MessageComposer';

jest.spyOn(Alert, 'alert');
jest.spyOn(AttachmentPickerUtils, 'useAttachmentPickerContext').mockImplementation(() => {
  const attachmentPickerStore = new AttachmentPickerStore();
  attachmentPickerStore.setSelectedPicker('images');
  return {
    AttachmentPickerSelectionBar,
    AttachmentPickerContent,
    closePicker: jest.fn(),
    openPicker: jest.fn(),
    setBottomInset: jest.fn(),
    setTopInset: jest.fn(),
    attachmentPickerStore,
  } as unknown as ReturnType<typeof AttachmentPickerUtils.useAttachmentPickerContext>;
});

const renderComponent = ({
  channelProps,
  client,
  props,
}: {
  channelProps: Partial<ChannelProps>;
  client: StreamChat;
  props: React.ComponentProps<typeof MessageComposer>;
}) => {
  return render(
    <OverlayProvider>
      <Chat client={client}>
        <Channel {...(channelProps as ChannelProps)}>
          <MessageComposer {...props} />
        </Channel>
      </Chat>
    </OverlayProvider>,
  );
};

describe('MessageComposer', () => {
  let client: StreamChat;
  let channel: ChannelType;

  beforeEach(async () => {
    jest.clearAllMocks();
    cleanup();
    const { client: chatClient, channels } = await initiateClientWithChannels();
    client = chatClient;
    channel = channels[0];
  });

  afterEach(() => {
    act(() => {
      channel.messageComposer.clear();
    });
  });

  it('should render MessageComposer', async () => {
    const channelProps = { channel };

    renderComponent({
      channelProps,
      client,
      props: {},
    });

    const { getByTestId, queryByTestId, queryByText } = screen;

    act(() => {
      fireEvent.press(getByTestId('attach-button'));
    });

    await waitFor(() => {
      expect(queryByTestId('upload-photo-touchable')).toBeTruthy();
      expect(queryByTestId('upload-file-touchable')).toBeTruthy();
      expect(queryByTestId('take-photo-touchable')).toBeTruthy();
      expect(queryByTestId('auto-complete-text-input')).toBeTruthy();
      expect(queryByText('Editing Message')).toBeFalsy();
    });
  });

  // TODO: Once the async audio design is done, fix it
  // it('should start the audio recorder on long press and cleanup on unmount', async () => {
  //   renderComponent({
  //     channelProps: { audioRecordingEnabled: false, channel }, // TODO: Once the async audio design is done, fix it
  //     client,
  //     props: {},
  //   });

  //   const { getByLabelText, unmount } = screen;

  //   const audioButton = getByLabelText('Start recording');

  //   act(() => {
  //     fireEvent(audioButton, 'longPress');
  //   });

  //   await waitFor(() => {
  //     expect(NativeHandlers.Audio.startRecording).toHaveBeenCalledTimes(1);
  //     expect(NativeHandlers.Audio.stopRecording).not.toHaveBeenCalled();
  //     expect(queryByTestId('recording-active-container')).toBeTruthy();
  //     expect(Alert.alert).not.toHaveBeenCalledWith('Hold to start recording.');
  //   });

  //   await act(() => {
  //     unmount();
  //   });

  //   await waitFor(() => {
  //     expect(NativeHandlers.Audio.stopRecording).toHaveBeenCalledTimes(1);
  //   });
  // });

  // it('should trigger an alert if a normal press happened on audio recording', async () => {
  //   renderComponent({
  //     channelProps: { audioRecordingEnabled: false, channel },
  //     client,
  //     props: {},
  //   });

  //   const { getByLabelText, queryByTestId } = screen;

  //   const audioButton = getByLabelText('Start recording');

  //   act(() => {
  //     fireEvent.press(audioButton);
  //   });

  //   await waitFor(() => {
  //     expect(NativeHandlers.Audio.startRecording).not.toHaveBeenCalled();
  //     expect(NativeHandlers.Audio.stopRecording).not.toHaveBeenCalled();
  //     expect(queryByTestId('recording-active-container')).not.toBeTruthy();
  //     // This is sort of a brittle test, but there doesn't seem to be another way
  //     // to target alerts. The reason why it's here is because we had a bug with it.
  //     expect(Alert.alert).toHaveBeenCalledWith('Hold to start recording.');
  //   });
  // });
});
