import React, { ComponentProps } from 'react';

import { Alert } from 'react-native';

import { act, cleanup, render, screen, waitFor } from '@testing-library/react-native';

import type { Channel as ChannelType, StreamChat } from 'stream-chat';
import { MessageComposer as StreamMessageComposer } from 'stream-chat';

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

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

import { generateLocalFileUploadAttachmentData } from '../../../mock-builders/attachments';

import { generateMessage } from '../../../mock-builders/generator/message';

import { AttachmentPickerStore } from '../../../state-store/attachment-picker-store';
import { AttachmentPickerContent } from '../../AttachmentPicker/components/AttachmentPickerContent';
import { AttachmentPickerSelectionBar } from '../../AttachmentPicker/components/AttachmentPickerSelectionBar';
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<ComponentProps<typeof Channel>> & { channel: ChannelType };
  client: StreamChat;
  props: Partial<ComponentProps<typeof MessageComposer>>;
}) => {
  return render(
    <OverlayProvider>
      <Chat client={client}>
        <Channel {...channelProps}>
          <MessageComposer {...props} />
        </Channel>
      </Chat>
    </OverlayProvider>,
  );
};

const editedMessageSetup = async ({
  composerConfig,
  composition,
}: {
  composerConfig?: ConstructorParameters<typeof StreamMessageComposer>[0]['config'];
  composition?: ConstructorParameters<typeof StreamMessageComposer>[0]['composition'];
} = {}) => {
  const { client: chatClient, channels } = await initiateClientWithChannels();
  const channel = channels[0];

  const messageComposer = new StreamMessageComposer({
    client: chatClient,
    composition,
    compositionContext: composition as unknown as ConstructorParameters<
      typeof StreamMessageComposer
    >[0]['compositionContext'],
    config: composerConfig,
  });

  messageComposer.registerSubscriptions();
  jest.spyOn(UseMessageComposerHooks, 'useMessageComposer').mockReturnValue(messageComposer);

  return { channel, chatClient, messageComposer };
};

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

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

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

  it('should render the SendMessageDisallowedIndicator if the send-message capability is not present', async () => {
    const props = {};
    const channelProps = { audioRecordingEnabled: true, channel };

    renderComponent({ channelProps, client, props });

    const { queryByTestId } = screen;

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeNull();
    });

    act(() => {
      client.dispatchEvent({
        cid: channel.data!.cid,
        own_capabilities: channel.data!.own_capabilities!.filter(
          (capability) => capability !== 'send-message',
        ),
        type: 'capabilities.changed',
      });
    });

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeTruthy();
    });
  });

  it('should not render the SendMessageDisallowedIndicator if the channel is frozen and the send-message capability is present', async () => {
    const props = {};
    const channelProps = { channel };

    renderComponent({ channelProps, client, props });

    const { queryByTestId } = screen;

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeNull();
    });
  });

  it('should render the SendMessageDisallowedIndicator in a frozen channel only if the send-message capability is not present', async () => {
    const props = {};
    const channelProps = { channel };

    renderComponent({ channelProps, client, props });

    const { queryByTestId } = screen;

    act(() => {
      client.dispatchEvent({
        channel: {
          ...channel.data,
          own_capabilities: channel.data!.own_capabilities!.filter(
            (capability: string) => capability !== 'send-message',
          ),
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
        } as any,
        cid: channel.data!.cid,
        type: 'channel.updated',
      });
    });

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeTruthy();
    });
  });
});

describe("SendMessageDisallowedIndicator's edited state", () => {
  it('should not render the SendMessageDisallowedIndicator if we are editing a message, regardless of capabilities', async () => {
    const message = generateMessage({
      attachments: [generateLocalFileUploadAttachmentData()],
      cid: 'messaging:channel-id',
      text: 'test',
    });

    const { channel: customChannel, chatClient } = await editedMessageSetup({
      composition: message,
    });

    await renderComponent({
      channelProps: { channel: customChannel },
      client: chatClient,
      props: {},
    });

    const { queryByTestId } = screen;

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeNull();
    });

    act(() => {
      chatClient.dispatchEvent({
        cid: customChannel.data!.cid,
        own_capabilities: customChannel.data!.own_capabilities!.filter(
          (capability: string) => capability !== 'send-message',
        ),
        type: 'capabilities.changed',
      });
    });

    await waitFor(() => {
      expect(queryByTestId('send-message-disallowed-indicator')).toBeNull();
    });
  });
});
