import { useEffect, useRef } from 'react';

import { GroupChannelEventSource, GroupChannelFilter, GroupChannelListOrder } from '@sendbird/chat/groupChannel';
import type { SendbirdBaseChannel, SendbirdChatSDK, SendbirdGroupChannelCollection } from '@sendbird/uikit-utils';
import { confirmAndMarkAsDelivered, useAsyncEffect, useFreshCallback, useUniqHandlerId } from '@sendbird/uikit-utils';

import { useAppFeatures } from '../../common/useAppFeatures';
import { useChannelHandler } from '../../handler/useChannelHandler';
import type { UseGroupChannelList, UseGroupChannelListOptions } from '../../types';
import { useGroupChannelListReducer } from './reducer';

const createGroupChannelListCollection = (
  sdk: SendbirdChatSDK,
  collectionCreator: UseGroupChannelListOptions['collectionCreator'],
) => {
  const passedCollection = collectionCreator?.();
  if (passedCollection) return passedCollection;

  const filter = new GroupChannelFilter();
  filter.includeEmpty = false;

  return sdk.groupChannel.createGroupChannelCollection({
    filter,
    limit: 20,
    order: GroupChannelListOrder.LATEST_LAST_MESSAGE,
  });
};

/**
 * @deprecated This hook is deprecated and will be replaced by the '@sendbird/uikit-tools' package.
 * */
export const useGroupChannelListWithCollection: UseGroupChannelList = (sdk, userId, options) => {
  const handlerId = useUniqHandlerId('useGroupChannelListWithCollection');
  const { deliveryReceiptEnabled } = useAppFeatures(sdk);

  const collectionRef = useRef<SendbirdGroupChannelCollection>();

  const { loading, groupChannels, refreshing, appendChannels, deleteChannels, updateRefreshing, updateLoading } =
    useGroupChannelListReducer();

  const updateChannelsAndMarkAsDelivered = (
    markAsDelivered: boolean,
    source?: GroupChannelEventSource,
    updatedChannels?: SendbirdBaseChannel[],
  ) => {
    const channels = collectionRef.current?.channels ?? [];
    appendChannels(channels, true);
    if (markAsDelivered && deliveryReceiptEnabled) {
      switch (source) {
        case GroupChannelEventSource.EVENT_MESSAGE_RECEIVED:
        case GroupChannelEventSource.EVENT_MESSAGE_SENT:
        case GroupChannelEventSource.SYNC_CHANNEL_BACKGROUND:
        case GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS:
        case undefined:
          confirmAndMarkAsDelivered(updatedChannels ?? channels);
          break;
      }
    }
  };

  const init = useFreshCallback(async (uid?: string) => {
    if (collectionRef.current) collectionRef.current?.dispose();

    if (uid) {
      collectionRef.current = createGroupChannelListCollection(sdk, options?.collectionCreator);

      collectionRef.current?.setGroupChannelCollectionHandler({
        onChannelsAdded: (context, channels) => {
          updateChannelsAndMarkAsDelivered(true, context.source, channels);
        },
        onChannelsUpdated: (context, channels) => {
          updateChannelsAndMarkAsDelivered(true, context.source, channels);
        },
        onChannelsDeleted: () => {
          updateChannelsAndMarkAsDelivered(false);
        },
      });

      if (collectionRef.current?.hasMore) {
        await collectionRef.current?.loadMore();
        updateChannelsAndMarkAsDelivered(true);
      }
    }
  });

  useEffect(() => {
    return () => {
      if (collectionRef.current) collectionRef.current?.dispose();
    };
  }, []);

  useAsyncEffect(async () => {
    updateLoading(true);
    await init(userId);
    updateLoading(false);
  }, [userId]);

  useChannelHandler(sdk, handlerId, {
    onUserBanned: (channel, user) => {
      const isMe = user.userId === userId;
      if (isMe) deleteChannels([channel.url]);
      else updateChannelsAndMarkAsDelivered(false);
    },
  });

  const refresh = useFreshCallback(async () => {
    updateRefreshing(true);
    await init(userId);
    updateRefreshing(false);
  });

  const next = useFreshCallback(async () => {
    if (collectionRef.current?.hasMore) {
      await collectionRef.current?.loadMore();
      updateChannelsAndMarkAsDelivered(true);
    }
  });

  return {
    loading,
    groupChannels,
    refresh,
    refreshing,
    next,
  };
};
