import { disableCache, enableCache } from '~/cache/api';
import { getChannels } from '../getChannels';

import * as getChannelMarkers from '../../../marker/api/getChannelMarkers';

import {
  client,
  connectClient,
  convertRawChannelPayload,
  disconnectClient,
  pause,
  channelQueryResponseWithMessagePreview,
  generateRawChannel,
  convertChannelFromRaw,
  channel2,
  generateRawMessage,
  generateRawSubChannel,
  user11,
  date,
  channelQueryResponseWithNoMessagePreview,
  channelGetResponseWithNoMessagePreview,
} from '~/utils/tests';
import { getChannelMessagePreviewWithUser } from '~/messagePreview/utils';
import * as getMessagePreviewSetting from '~/client/utils/messagePreviewEngine';

const getSnapshot = (params?: Record<string, any>) => {
  return {
    data: [] as Amity.Channel[],
    loading: true,
    error: undefined as any,
    ...params,
  };
};

const rawMessage = generateRawMessage({
  channelId: 'channelId11',
  channelPublicId: 'channelId11',
  messageFeedId: 'channelId11',
  messageId: 'messageId1',
  creatorId: 'test',
  creatorPublicId: 'test',
});

const rawNewCreatedMessage = generateRawMessage({
  channelId: 'channelId11',
  channelPublicId: 'channelId11',
  messageFeedId: 'channelId11',
  messageId: 'messageId2',
  creatorId: 'test',
  creatorPublicId: 'test',
});

const rawNewChannelWithMessagePreview = generateRawChannel({
  type: 'conversation',
  messagePreviewId: 'messageId2',
});

const rawSubChannel = generateRawSubChannel({
  channelId: 'channelId11',
  messageFeedId: 'channelId11',
  messagePreviewId: 'messageId2',
  name: 'subChannel1',
});

const messagePreviewFromResponse = channelQueryResponseWithMessagePreview.data.messagePreviews[0];
const messageFeedsInfoFromResponse =
  channelQueryResponseWithMessagePreview.data.messageFeedsInfo[0];

const messagePreview = {
  channelId: messagePreviewFromResponse.channelPublicId,
  subChannelId: messagePreviewFromResponse.messageFeedId,
  dataType: messagePreviewFromResponse.dataType,
  isDeleted: messagePreviewFromResponse.isDeleted,
  segment: messagePreviewFromResponse.segment,
  creatorId: messagePreviewFromResponse.creatorPublicId,
  createdAt: messagePreviewFromResponse.createdAt,
  data: messagePreviewFromResponse.data,
  updatedAt: messagePreviewFromResponse.updatedAt!,
  subChannelName: messageFeedsInfoFromResponse.name!,
  subChannelUpdatedAt: messageFeedsInfoFromResponse.updatedAt!,
  messagePreviewId: messageFeedsInfoFromResponse.messagePreviewId!,
  user: user11,
};

const newMessagePreview = {
  channelId: rawNewCreatedMessage.channelId,
  creatorId: rawNewCreatedMessage.creatorPublicId,
  messagePreviewId: rawNewCreatedMessage.messageId,
  createdAt: rawNewCreatedMessage.createdAt,
  updatedAt: rawNewCreatedMessage.updatedAt,
  data: rawNewCreatedMessage.data,
  dataType: rawNewCreatedMessage.dataType,
  isDeleted: rawNewCreatedMessage.isDeleted,
  segment: rawNewCreatedMessage.segment,
  subChannelId: rawSubChannel.messageFeedId,
  subChannelName: rawSubChannel.name,
  subChannelUpdatedAt: rawSubChannel.updatedAt!,
  user: user11,
};

const rawUpdatedMessage: Amity.RawMessage = {
  ...rawMessage,
  createdAt: messagePreviewFromResponse.createdAt,
  data: {
    text: 'updated',
  },
  updatedAt: date,
};

const rawDeletedMessage: Amity.RawMessage = {
  ...rawMessage,
  createdAt: messagePreviewFromResponse.createdAt,
  data: {
    text: 'updated',
  },
  isDeleted: true,
  updatedAt: date,
};

const updatedMessagePreview = {
  ...messagePreview,
  data: rawUpdatedMessage.data,
  updatedAt: rawUpdatedMessage.updatedAt,
};

const deletedMessagePreview = {
  ...updatedMessagePreview,
  isDeleted: true,
};

const rawUpdatedSubChannel = {
  ...rawSubChannel,
  name: 'subChannel1-edit',
  updatedAt: date,
};

const rawDeletedSubChannel = {
  ...rawSubChannel,
  isDeleted: true,
  updatedAt: date,
};

const updatedSubChannelMessagePreview = {
  ...messagePreview,
  subChannelName: rawUpdatedSubChannel.name,
  updatedAt: rawUpdatedSubChannel.updatedAt,
};

describe('getChannels with MessagePreview', () => {
  jest.spyOn(getChannelMarkers, 'getChannelMarkers').mockResolvedValue({
    data: [],
    cachedAt: undefined,
  });

  describe('Message Preview include delete', () => {
    beforeAll(() => {
      jest
        .spyOn(getMessagePreviewSetting, 'getMessagePreviewSetting')
        .mockResolvedValue(Amity.MessagePreviewSetting.MESSAGE_PREVIEW_INCLUDE_DELETED);

      connectClient();
    });

    afterAll(() => {
      disconnectClient();
      jest.clearAllMocks();
    });

    beforeEach(enableCache);
    afterEach(disableCache);
    describe('getChannels', () => {
      test('it should return channel collection with message preview', async () => {
        const callback = jest.fn();
        client.http.get = jest.fn().mockResolvedValue(channelQueryResponseWithMessagePreview);

        getChannels({}, callback);
        await pause();

        expect(callback).toHaveBeenCalledTimes(2);
        expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
        expect(callback).toHaveBeenNthCalledWith(
          2,
          expect.objectContaining(
            getSnapshot({
              data: convertRawChannelPayload(
                channelQueryResponseWithMessagePreview.data,
              ).channels.map(getChannelMessagePreviewWithUser),
              loading: false,
            }),
          ),
        );
      });
    });

    describe('events', () => {
      beforeAll(() => {
        client.getMessagePreviewSetting = jest
          .fn()
          .mockResolvedValue(Amity.MessagePreviewSetting.MESSAGE_PREVIEW_INCLUDE_DELETED);
      });

      const cases: [string, keyof Amity.Events, ValueOf<Amity.Events>, Amity.Channel[]][] = [
        [
          'it should update message preview to collection onMessageCreated',
          'message.created',
          {
            messages: [rawNewCreatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertChannelFromRaw(rawNewChannelWithMessagePreview),
              lastActivity: rawNewCreatedMessage.createdAt,
              messagePreview: newMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onMessageUpdated',
          'message.updated',
          {
            messages: [rawUpdatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertChannelFromRaw(rawNewChannelWithMessagePreview),
              lastActivity: channelQueryResponseWithMessagePreview.data.channels[0].lastActivity,
              messagePreviewId: updatedMessagePreview.messagePreviewId,
              messagePreview: updatedMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onMessageDeleted',
          'message.deleted',
          {
            messages: [rawDeletedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertChannelFromRaw(rawNewChannelWithMessagePreview),
              lastActivity: channelQueryResponseWithMessagePreview.data.channels[0].lastActivity,
              messagePreviewId: deletedMessagePreview.messagePreviewId,
              messagePreview: deletedMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onSubChannelUpdated',
          'message-feed.updated',
          {
            messageFeeds: [rawUpdatedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
          [
            {
              ...convertRawChannelPayload(channelQueryResponseWithMessagePreview.data).channels[0],
              messagePreviewId: updatedSubChannelMessagePreview.messagePreviewId,
              messagePreview: updatedSubChannelMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onSubChannelDeleled',
          'message-feed.deleted',
          {
            messageFeeds: [rawDeletedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
          [
            {
              ...convertRawChannelPayload(channelQueryResponseWithNoMessagePreview.data)
                .channels[0],
              messagePreviewId: undefined,
              messagePreview: null,
            },
            channel2,
          ],
        ],
      ];

      test.each(cases)('%s', async (test, event, eventPayload, expected) => {
        const callback = jest.fn();
        client.http.get = jest.fn().mockResolvedValue(channelQueryResponseWithMessagePreview);

        getChannels({ isDeleted: false }, callback);
        await pause();

        if (event === 'message-feed.deleted') {
          // eslint-disable-next-line require-atomic-updates
          client.http.get = jest.fn().mockResolvedValue(channelGetResponseWithNoMessagePreview);

          await pause();
        }

        client.emitter.emit(event, eventPayload);
        await pause();

        expect(callback).toHaveBeenNthCalledWith(
          3,
          expect.objectContaining(
            getSnapshot({
              data: expected,
              loading: false,
              hasNextPage: true,
            }),
          ),
        );
      });
    });
  });

  describe('Message Preview not include delete', () => {
    beforeAll(() => {
      jest
        .spyOn(getMessagePreviewSetting, 'getMessagePreviewSetting')
        .mockResolvedValue(Amity.MessagePreviewSetting.MESSAGE_PREVIEW_NOT_INCLUDE_DELETED);

      connectClient();
    });

    afterAll(() => {
      disconnectClient();
      jest.clearAllMocks();
    });

    beforeEach(enableCache);
    afterEach(disableCache);

    describe('events', () => {
      beforeAll(() => {
        client.getMessagePreviewSetting = jest
          .fn()
          .mockResolvedValue(Amity.MessagePreviewSetting.MESSAGE_PREVIEW_NOT_INCLUDE_DELETED);
      });

      const cases: [string, keyof Amity.Events, ValueOf<Amity.Events>, Amity.Channel[]][] = [
        [
          'it should update message preview to collection onMessageCreated',
          'message.created',
          {
            messages: [rawNewCreatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertChannelFromRaw(rawNewChannelWithMessagePreview),
              lastActivity: rawNewCreatedMessage.createdAt,
              messagePreview: newMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onMessageUpdated',
          'message.updated',
          {
            messages: [rawUpdatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertChannelFromRaw(rawNewChannelWithMessagePreview),
              lastActivity: channelQueryResponseWithMessagePreview.data.channels[0].lastActivity,
              messagePreviewId: updatedMessagePreview.messagePreviewId,
              messagePreview: updatedMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onMessageDeleted',
          'message.deleted',
          {
            messages: [rawDeletedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
          [
            {
              ...convertRawChannelPayload(channelQueryResponseWithNoMessagePreview.data)
                .channels[0],
              messagePreviewId: undefined,
              messagePreview: null,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onSubChannelUpdated',
          'message-feed.updated',
          {
            messageFeeds: [rawUpdatedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
          [
            {
              ...convertRawChannelPayload(channelQueryResponseWithMessagePreview.data).channels[0],
              messagePreviewId: updatedSubChannelMessagePreview.messagePreviewId,
              messagePreview: updatedSubChannelMessagePreview,
            },
            channel2,
          ],
        ],
        [
          'it should update message preview to collection onSubChannelDeleled',
          'message-feed.deleted',
          {
            messageFeeds: [rawDeletedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
          [
            {
              ...convertRawChannelPayload(channelQueryResponseWithNoMessagePreview.data)
                .channels[0],
              messagePreviewId: undefined,
              messagePreview: null,
            },
            channel2,
          ],
        ],
      ];

      test.each(cases)('%s', async (test, event, eventPayload, expected) => {
        const callback = jest.fn();
        client.http.get = jest.fn().mockResolvedValue(channelQueryResponseWithMessagePreview);

        getChannels({ isDeleted: false }, callback);
        await pause();

        if (event === 'message.deleted' || event === 'message-feed.deleted') {
          // eslint-disable-next-line require-atomic-updates
          client.http.get = jest.fn().mockResolvedValue(channelGetResponseWithNoMessagePreview);

          await pause();
        }

        client.emitter.emit(event, eventPayload);
        await pause();

        expect(callback).toHaveBeenNthCalledWith(
          3,
          expect.objectContaining(
            getSnapshot({
              data: expected,
              loading: false,
              hasNextPage: true,
            }),
          ),
        );
      });
    });
  });

  describe('No Message Preview', () => {
    beforeAll(() => {
      jest
        .spyOn(getMessagePreviewSetting, 'getMessagePreviewSetting')
        .mockResolvedValue(Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW);

      connectClient();
    });

    afterAll(() => {
      disconnectClient();
      jest.clearAllMocks();
    });

    beforeEach(enableCache);
    afterEach(disableCache);

    describe('events', () => {
      beforeAll(() => {
        client.getMessagePreviewSetting = jest
          .fn()
          .mockResolvedValue(Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW);
      });

      const cases: [string, keyof Amity.Events, ValueOf<Amity.Events>][] = [
        [
          'it should update message preview to collection onMessageCreated',
          'message.created',
          {
            messages: [rawNewCreatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
        ],
        [
          'it should update message preview to collection onMessageUpdated',
          'message.updated',
          {
            messages: [rawUpdatedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
        ],
        [
          'it should update message preview to collection onMessageDeleted',
          'message.deleted',
          {
            messages: [rawDeletedMessage],
            messageFeeds: [rawSubChannel],
            users: [],
            files: [],
            reactions: [],
          },
        ],
        [
          'it should update message preview to collection onSubChannelUpdated',
          'message-feed.updated',
          {
            messageFeeds: [rawUpdatedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
        ],
        [
          'it should update message preview to collection onSubChannelDeleled',
          'message-feed.deleted',
          {
            messageFeeds: [rawDeletedSubChannel],
            messages: [],
            users: [],
            files: [],
          },
        ],
      ];

      test.each(cases)('%s', async (test, event, eventPayload) => {
        const callback = jest.fn();
        client.http.get = jest.fn().mockResolvedValue(channelQueryResponseWithNoMessagePreview);

        getChannels({ isDeleted: false }, callback);
        await pause();

        client.emitter.emit(event, eventPayload);
        await pause();

        expect(callback).toHaveBeenCalledTimes(2);
        expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
        expect(callback).toHaveBeenNthCalledWith(
          2,
          expect.objectContaining(
            getSnapshot({
              data: convertRawChannelPayload(
                channelQueryResponseWithNoMessagePreview.data,
              ).channels.map(getChannelMessagePreviewWithUser),
              loading: false,
            }),
          ),
        );
      });
    });
  });
});
