import {useCallback, useRef} from 'react';
import {useTranslation} from 'react-i18next';
import {useMutation} from 'react-query';
import {
  LocalEvent,
  localEventsHandler,
  LocalEventTypes,
} from '@config/synchroniser';
import {
  ChatMuteOption,
  LOCAL_INITIAL_VERSION,
  MAX_PINNED_CONVERSATIONS,
} from '@modules/chats/constants';
import {closeBottomSheet} from '@modules/app/redux/actions';
import {setPinLimitReached} from '@modules/chats/store';
import {kickUserFromConversation} from '@modules/chats/services';
import {ChatNotificationType} from '@modules/chats/interfaces';
import {
  isCurrentUserTheOnlyAdmin,
  getPinnedConversationsCount,
} from '@modules/chats/db';
import {ConversationsUpdaterService} from '@modules/chats/instances/updaters';
import {dispatch} from '@redux/store';
import {showSnackbar} from '@redux/dispatchers';
import {persistedUserId} from '@services/localStorage';
import {
  addHours,
  getUlid,
  addDays,
  addMinutes,
  logAmplitudeEvent,
} from '@shared/utils';
import {AMPLITUDE_EVENTS} from '@shared/constants';

import {useGroups} from '../store/zustand/useGroups';
import {conversationMarker} from '../instances/ConversationMarker';

const getTimeFromMuteOption = (time: ChatMuteOption): Nullable<number> => {
  const now = Date.now();
  switch (time) {
    case '1m':
      return addMinutes(now, 1).getTime();
    case '8h':
      return addHours(now, 8).getTime();
    case '1w':
      return addDays(now, 7).getTime();
    case 'always':
      return null;
  }
};

export const useConversationActions = () => {
  const {t} = useTranslation();
  const userId = persistedUserId!;
  const conversationNameRef = useRef('');

  const {mutate: leaveGroupMutation} = useMutation(
    ({channel}: {channel: string; onSuccess?: () => void}) =>
      kickUserFromConversation({
        channel,
        user: String(userId),
        idempotencyKey: getUlid(),
      }),
    {
      onSuccess: (_, {channel, onSuccess}) => {
        dispatch(closeBottomSheet());

        ConversationsUpdaterService.leaveGroup({
          channelId: channel,
          triggeredBy: userId,
        });
        showSnackbar({
          title: t('chat.groups.manage.after_exit_group', {
            value: conversationNameRef.current || '',
          }),
          variant: 'info',
        });
        onSuccess?.();
      },
    },
  );

  /**
   * Kick current user from a group.
   * If the user is an admin and there is only one admin, cancel the action,
   * otherwise, leave the group.
   */
  const leaveGroup = useCallback(
    async ({channel, onSuccess}: {channel: string; onSuccess?: () => void}) => {
      const conversation = useGroups.getState().groups[channel]?.groupInfo;
      const isOnlyAdmin = await isCurrentUserTheOnlyAdmin(channel);

      if (isOnlyAdmin) {
        return;
      }

      // Save the conversation name to use it in the snackbar after, since the conversation object could be undefined from store
      conversationNameRef.current = conversation.name || '';

      leaveGroupMutation({
        channel,
        onSuccess,
      });
    },
    [leaveGroupMutation],
  );

  /**
   * Mute or unmute a chat.
   * @param type - The type to perform (see `ChatNotificationType`)
   * @param time - The time to mute the chat for (1m (only for development), 8h, 1w, `null` for always)
   */
  const changeNotificationType = useCallback(
    async ({
      channel,
      type,
      time,
    }: {
      channel: string;
      type: ChatNotificationType;
      time?: ChatMuteOption;
    }) => {
      const conversation = useGroups.getState().groups[channel]?.groupInfo;
      if (!conversation) {
        return;
      }

      logAmplitudeEvent(
        type === ChatNotificationType.All
          ? AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_UNMUTED
          : AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_MUTED,
        {
          userId,
          conversationType: conversation?.is_channel ? 'group' : 'individual',
          duration: time,
          conversationId: channel,
        },
      );

      const until = time ? getTimeFromMuteOption(time) : undefined;
      ConversationsUpdaterService.updateProperties(channel, {
        version: LOCAL_INITIAL_VERSION,
        notification_status: {
          notification_type: type,
          until,
        },
      });
      const localEvent: LocalEvent = {
        id: channel,
        type: LocalEventTypes.CHATS2_SET_NOTIFICATION_TYPE,
        unique: true,
        data: {
          channel,
          idempotencyKey: getUlid(),
          properties: {
            ...(conversation?.properties || {}),
            version: conversation?.properties?.version || LOCAL_INITIAL_VERSION,
            notification_status: {
              notification_type: type,
              until,
            },
          },
        },
      };
      await localEventsHandler.processEvent(localEvent);
    },
    [userId],
  );

  /**
   * Pin or unpin a chat.
   */
  const togglePin = useCallback(
    async ({channel}: {channel: string}) => {
      const conversation = useGroups.getState().groups[channel]?.groupInfo;
      if (!conversation) {
        return;
      }

      const pinned_at = !conversation.properties?.pinned_at ? Date.now() : null;
      const pinnedConversationsCount = await getPinnedConversationsCount();

      if (pinnedConversationsCount >= MAX_PINNED_CONVERSATIONS && pinned_at) {
        setPinLimitReached(true);
        return;
      }

      logAmplitudeEvent(
        pinned_at
          ? AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_PINNED
          : AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_UNPINNED,
        {
          userId,
          conversationType: conversation?.is_channel ? 'group' : 'individual',
          conversationId: channel,
        },
      );

      ConversationsUpdaterService.updateProperties(channel, {
        version: LOCAL_INITIAL_VERSION,
        pinned_at,
      });

      const localEvent: LocalEvent = {
        id: channel,
        type: LocalEventTypes.CHATS2_SET_PINNED,
        unique: true,
        data: {
          channel,
          idempotencyKey: getUlid(),
          properties: {
            ...(conversation?.properties || {}),
            version: conversation?.properties?.version || LOCAL_INITIAL_VERSION,
            pinned_at,
          },
        },
      };

      await localEventsHandler.processEvent(localEvent);
    },
    [userId],
  );

  const markAsUnread = useCallback(
    async ({channel, unreadCount}: {channel: string; unreadCount: number}) => {
      const conversation = useGroups.getState().groups[channel]?.groupInfo;
      if (!conversation) {
        return;
      }

      const unreadValue = !conversation.properties?.marked_unread_at
        ? Date.now()
        : null;

      const isMarkAsUnread = unreadValue && unreadCount === 0;
      logAmplitudeEvent(
        isMarkAsUnread
          ? AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_MARKED_UNREAD
          : AMPLITUDE_EVENTS.CHATS_2_CONVERSATION_MARKED_READ,
        {
          userId,
          conversationType: conversation?.is_channel ? 'group' : 'individual',
          conversationId: channel,
        },
      );

      ConversationsUpdaterService.updateProperties(channel, {
        version: LOCAL_INITIAL_VERSION,
        marked_unread_at: unreadCount === 0 ? unreadValue : null,
      });
      if (isMarkAsUnread) {
        const localEvent: LocalEvent = {
          id: channel,
          type: LocalEventTypes.CHATS2_MARK_UNREAD,
          unique: true,
          data: {
            channel,
            idempotencyKey: getUlid(),
            properties: {
              ...(conversation?.properties || {}),
              version:
                conversation?.properties?.version || LOCAL_INITIAL_VERSION,
              marked_unread_at: unreadValue,
            },
          },
        };
        await localEventsHandler.processEvent(localEvent);
      } else {
        conversationMarker.markConversationAsRead(channel);
      }
    },
    [userId],
  );

  return {
    changeNotificationType,
    leaveGroup,
    togglePin,
    markAsUnread,
  };
};
