import { type SetStateAction, useEffect, useMemo, useState } from 'react';

import throttle from 'lodash/throttle';
import type { Channel, Event, LocalMessage, MessageResponse, StreamChat } from 'stream-chat';

import { useIsChannelMuted } from './useIsChannelMuted';
import { useIsChannelPinned } from './useIsChannelPinned';

import { useChannelsContext } from '../../../contexts';
import { useStableCallback } from '../../../hooks';

const setLastMessageThrottleTimeout = 500;
const setLastMessageThrottleOptions = { leading: true, trailing: true };

const refreshUnreadCountThrottleTimeout = 400;
const refreshUnreadCountThrottleOptions = setLastMessageThrottleOptions;

export type LastMessageType = LocalMessage | MessageResponse;

export const useChannelPreviewData = (
  channel: Channel,
  client: StreamChat,
  forceUpdateOverride?: number,
) => {
  const [forceUpdate, setForceUpdate] = useState(0);
  const [lastMessage, setLastMessageInner] = useState<LastMessageType>(
    () => channel.state.latestMessages[channel.state.latestMessages.length - 1],
  );
  const throttledSetLastMessage = useMemo(
    () =>
      throttle(
        (newLastMessage: SetStateAction<LastMessageType>) => setLastMessageInner(newLastMessage),
        setLastMessageThrottleTimeout,
        setLastMessageThrottleOptions,
      ),
    [],
  );
  const setLastMessage = useStableCallback((newLastMessage: SetStateAction<LastMessageType>) =>
    throttledSetLastMessage(newLastMessage),
  );
  const [unread, setUnread] = useState(channel.countUnread());
  const { muted } = useIsChannelMuted(channel);
  const pinned = useIsChannelPinned(channel);
  const { forceUpdate: contextForceUpdate } = useChannelsContext();
  const channelListForceUpdate = forceUpdateOverride ?? contextForceUpdate;

  const channelLastMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
  const channelLastMessageString = `${channelLastMessage?.id}${channelLastMessage?.updated_at}`;

  const refreshUnreadCount = useMemo(
    () =>
      throttle(
        () => {
          if (muted) {
            setUnread(0);
          } else {
            setUnread(channel.countUnread());
          }
        },
        refreshUnreadCountThrottleTimeout,
        refreshUnreadCountThrottleOptions,
      ),
    [channel, muted],
  );

  useEffect(() => {
    const unsubscribe = channel.messageComposer.registerDraftEventSubscriptions();
    return () => unsubscribe();
  }, [channel.messageComposer]);

  useEffect(() => {
    const { unsubscribe } = client.on('notification.mark_read', () => {
      refreshUnreadCount();
    });
    return unsubscribe;
  }, [client, refreshUnreadCount]);

  useEffect(() => {
    setLastMessage((prevLastMessage) =>
      channelLastMessage &&
      (channelLastMessage.id !== prevLastMessage?.id ||
        channelLastMessage.updated_at !== prevLastMessage?.updated_at)
        ? channelLastMessage
        : prevLastMessage,
    );
    refreshUnreadCount();
  }, [
    channelLastMessage,
    channelLastMessageString,
    channelListForceUpdate,
    setLastMessage,
    refreshUnreadCount,
  ]);

  /**
   * This effect listens for the `notification.mark_read` event and sets the unread count to 0
   */
  useEffect(() => {
    const handleReadEvent = (event: Event) => {
      if (!event.cid) {
        return;
      }
      if (channel.cid !== event.cid) {
        return;
      }
      if (event?.user?.id === client.userID) {
        setUnread(0);
      } else if (event?.user?.id) {
        setForceUpdate((prev) => prev + 1);
      }
    };
    const readSubscription = client.on('message.read', handleReadEvent);
    // `message.read_locally` is the client-only equivalent emitted by `channel.markReadLocally()` when
    // read events are disabled (e.g. livestreams with `isLocalUnreadCountEnabled`).
    const localReadSubscription = client.on('message.read_locally', handleReadEvent);
    return () => {
      readSubscription.unsubscribe();
      localReadSubscription.unsubscribe();
    };
  }, [client, channel]);

  /**
   * This effect listens for the `notification.mark_unread` event and updates the unread count
   */
  useEffect(() => {
    const handleUnreadEvent = (event: Event) => {
      if (!event.cid) {
        return;
      }
      if (channel.cid !== event.cid) {
        return;
      }
      if (event.user?.id !== client.user?.id) {
        return;
      }
      setUnread(channel.countUnread());
    };
    const { unsubscribe } = client.on('notification.mark_unread', handleUnreadEvent);
    return unsubscribe;
  }, [client, channel]);

  /**
   * This effect listens for the `message.new`, `message.updated`, `message.deleted`, `message.undeleted`, and `channel.truncated` events
   */
  useEffect(() => {
    refreshUnreadCount();

    const handleEvent = () => {
      setLastMessage(channel.state.latestMessages[channel.state.latestMessages.length - 1]);
      refreshUnreadCount();
    };

    const handleNewMessageEvent = (event: Event) => {
      const message = event.message;
      if (message && (!message.parent_id || message.show_in_channel)) {
        setLastMessage(message);
        refreshUnreadCount();
      }
    };

    const handleUpdatedOrDeletedMessage = (event: Event) => {
      setLastMessage((prevLastMessage) => {
        if (prevLastMessage?.id === event.message?.id) {
          return event.message;
        }
        return prevLastMessage;
      });
    };

    const listeners = [
      channel.on('message.new', handleNewMessageEvent),
      channel.on('message.updated', handleUpdatedOrDeletedMessage),
      channel.on('message.deleted', handleUpdatedOrDeletedMessage),
      channel.on('message.undeleted', handleEvent),
      channel.on('channel.truncated', handleEvent),
    ];

    return () => listeners.forEach((l) => l.unsubscribe());
  }, [channel, refreshUnreadCount, forceUpdate, channelListForceUpdate, setLastMessage]);

  return { lastMessage, muted, pinned, unread };
};
