import { useEffect, useRef, useState } from 'react';
import { NotificationCountType, Room, RoomEvent, Thread, ThreadEvent } from 'matrix-js-sdk';

// Cheap signature over the fields the Threads list actually renders on (id,
// last-activity ts, reply count, unread, participation, and a root-edit token).
// We only push a new snapshot when this changes, so noisy repeated
// ThreadEvent.Update / UnreadNotifications re-emits that don't alter the list
// don't cause a re-render.
// `replyToEvent` reads the server bundle's latest_event, so last-activity is
// accurate immediately (before/without lazily paginating each thread's replies);
// `length` covers reply-count changes (e.g. a mid-thread redaction) and
// `replacingEventId` covers a root-message edit changing the row snippet.
const signatureOf = (room: Room, threads: Thread[]): string =>
  threads
    .map((t) => {
      const ts = t.replyToEvent?.getTs() ?? t.rootEvent?.getTs() ?? 0;
      const unread = room.getThreadUnreadNotificationCount(t.id, NotificationCountType.Total) ?? 0;
      const rootEdit = t.rootEvent?.replacingEventId() ?? '';
      const participated = t.hasCurrentUserParticipated ? 1 : 0;
      return `${t.id}:${ts}:${t.length}:${unread}:${participated}:${rootEdit}`;
    })
    .sort()
    .join('|');

/**
 * Live list of a room's threads. Populates the full server list once via
 * `fetchRoomThreads`, then keeps a snapshot in sync with the room's thread and
 * unread events. Returns the raw `Thread[]`; the caller derives/filters/sorts.
 */
export function useRoomThreads(room: Room): Thread[] {
  const [threads, setThreads] = useState<Thread[]>(() => room.getThreads());
  const sigRef = useRef<string>('');

  useEffect(() => {
    let cancelled = false;

    const refresh = () => {
      if (cancelled) return;
      const next = room.getThreads();
      const sig = signatureOf(room, next);
      if (sig === sigRef.current) return;
      sigRef.current = sig;
      setThreads(next);
    };

    // Force the first push, then pull the full server-known thread list.
    sigRef.current = '';
    refresh();
    room
      .fetchRoomThreads()
      .then(refresh)
      .catch(() => undefined);

    room.on(ThreadEvent.New, refresh);
    room.on(ThreadEvent.NewReply, refresh);
    room.on(ThreadEvent.Update, refresh);
    room.on(ThreadEvent.Delete, refresh);
    room.on(RoomEvent.UnreadNotifications, refresh);
    return () => {
      cancelled = true;
      room.removeListener(ThreadEvent.New, refresh);
      room.removeListener(ThreadEvent.NewReply, refresh);
      room.removeListener(ThreadEvent.Update, refresh);
      room.removeListener(ThreadEvent.Delete, refresh);
      room.removeListener(RoomEvent.UnreadNotifications, refresh);
    };
  }, [room]);

  return threads;
}
