import {
  MatrixEvent,
  MatrixEventEvent,
  MatrixEventHandlerMap,
  Room,
  RoomEvent,
  RoomEventHandlerMap,
} from 'matrix-js-sdk';
import { useEffect, useState } from 'react';
import { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings';
import { MessageEvent, StateEvent } from '../../types/matrix/room';
import { isMembershipChanged, reactionOrEditEvent } from '../utils/room';

export const useRoomLatestRenderedEvent = (room: Room, enabled = true) => {
  const [hideMembershipEvents] = useSetting(settingsAtom, 'hideMembershipEvents');
  const [hideNickAvatarEvents] = useSetting(settingsAtom, 'hideNickAvatarEvents');
  const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
  const [latestEvent, setLatestEvent] = useState<MatrixEvent>();

  useEffect(() => {
    // The latest-event preview is only rendered for DM rooms. Skip the work —
    // and especially the GLOBAL client `Decrypted` listener, which every nav
    // item would otherwise add and which fans out on every decryption app-wide
    // (PERF-5) — when the caller isn't going to use the result.
    if (!enabled) {
      setLatestEvent(undefined);
      return undefined;
    }

    const getLatestEvent = (): MatrixEvent | undefined => {
      const liveEvents = room.getLiveTimeline().getEvents();
      for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
        const evt = liveEvents[i];

        if (!evt) continue;
        if (reactionOrEditEvent(evt)) continue;
        if (evt.getType() === StateEvent.RoomMember) {
          const membershipChanged = isMembershipChanged(evt);
          if (membershipChanged && hideMembershipEvents) continue;
          if (!membershipChanged && hideNickAvatarEvents) continue;
          return evt;
        }

        if (
          evt.getType() === MessageEvent.RoomMessage ||
          evt.getType() === MessageEvent.RoomMessageEncrypted ||
          evt.getType() === MessageEvent.Sticker ||
          evt.getType() === StateEvent.RoomName ||
          evt.getType() === StateEvent.RoomTopic ||
          evt.getType() === StateEvent.RoomAvatar
        ) {
          return evt;
        }

        if (showHiddenEvents) return evt;
      }
      return undefined;
    };

    const handleTimelineEvent: RoomEventHandlerMap[RoomEvent.Timeline] = () => {
      setLatestEvent(getLatestEvent());
    };
    // An E2EE message often arrives as an undecrypted placeholder and is decrypted
    // shortly after — decryption does NOT re-fire RoomEvent.Timeline, so without this
    // the DM preview stays stale ("Encrypted message") until the next timeline event.
    const handleDecrypted: MatrixEventHandlerMap[MatrixEventEvent.Decrypted] = (event) => {
      if (event.getRoomId() !== room.roomId) return;
      setLatestEvent(getLatestEvent());
    };
    setLatestEvent(getLatestEvent());

    room.on(RoomEvent.Timeline, handleTimelineEvent);
    room.client.on(MatrixEventEvent.Decrypted, handleDecrypted);
    return () => {
      room.removeListener(RoomEvent.Timeline, handleTimelineEvent);
      room.client.removeListener(MatrixEventEvent.Decrypted, handleDecrypted);
    };
  }, [room, enabled, hideMembershipEvents, hideNickAvatarEvents, showHiddenEvents]);

  return latestEvent;
};
