import { useEffect, useMemo, useRef } from 'react';
import {
  EventType,
  MatrixEvent,
  RelationType,
  Room,
  RoomEvent,
} from 'matrix-js-sdk';
import { useMatrixClient } from '../../hooks/useMatrixClient';

type ForumRoomLiveOptions = {
  /** When set, only sync for events in this thread root. */
  threadRootId?: string;
  /** Debounce window in ms. */
  wait?: number;
};

function isForumThreadEvent(mEvent: MatrixEvent, threadRootId: string): boolean {
  if (mEvent.getId() === threadRootId) return true;

  const relation = mEvent.getRelation();
  if (relation?.rel_type === RelationType.Thread && relation.event_id === threadRootId) {
    return true;
  }

  const content = mEvent.getContent();
  const relates = content['m.relates_to'];
  if (relates && typeof relates === 'object') {
    const rel = relates as { rel_type?: string; event_id?: string };
    if (rel.rel_type === RelationType.Thread && rel.event_id === threadRootId) {
      return true;
    }
  }

  if (relation?.rel_type === RelationType.Replace && relation.event_id === threadRootId) {
    return true;
  }

  return false;
}

function isSecondaryMatrixEvent(mEvent: MatrixEvent): boolean {
  const relation = mEvent.getRelation();
  return (
    relation?.rel_type === RelationType.Replace ||
    relation?.rel_type === RelationType.Annotation
  );
}

function isForumFeedEvent(mEvent: MatrixEvent): boolean {
  const type = mEvent.getType();
  return type === EventType.RoomMessage || type === EventType.RoomMessageEncrypted;
}

function isRelevantEvent(mEvent: MatrixEvent, threadRootId?: string): boolean {
  if (mEvent.getType() === EventType.Reaction) return false;
  if (isSecondaryMatrixEvent(mEvent)) {
    if (!threadRootId) return false;
    const targetId = mEvent.getRelation()?.event_id;
    return targetId === threadRootId;
  }
  if (threadRootId) {
    return isForumThreadEvent(mEvent, threadRootId);
  }
  return isForumFeedEvent(mEvent);
}

/** Debounced live sync when topic room timelines change. */
export function useForumRoomLiveUpdates(
  rooms: Room[],
  onUpdate: () => void,
  options: ForumRoomLiveOptions = {}
) {
  const mx = useMatrixClient();
  const { threadRootId, wait = 200 } = options;
  const onUpdateRef = useRef(onUpdate);
  onUpdateRef.current = onUpdate;

  const roomIds = useMemo(() => new Set(rooms.map((room) => room.roomId)), [rooms]);
  const roomKey = useMemo(() => [...roomIds].sort().join(','), [roomIds]);

  useEffect(() => {
    if (!roomKey) return undefined;

    let timeoutId: number | undefined;

    const scheduleUpdate = () => {
      if (timeoutId) window.clearTimeout(timeoutId);
      timeoutId = window.setTimeout(() => {
        onUpdateRef.current();
      }, wait);
    };

    const handleTimeline = (mEvent: MatrixEvent, eventRoom?: Room) => {
      if (!eventRoom || !roomIds.has(eventRoom.roomId)) return;
      if (!isRelevantEvent(mEvent, threadRootId)) return;
      scheduleUpdate();
    };

    const handleRedaction = (_mEvent: MatrixEvent, eventRoom?: Room) => {
      if (!eventRoom || !roomIds.has(eventRoom.roomId)) return;
      scheduleUpdate();
    };

    mx.on(RoomEvent.Timeline, handleTimeline);
    mx.on(RoomEvent.Redaction, handleRedaction);

    return () => {
      if (timeoutId) window.clearTimeout(timeoutId);
      mx.removeListener(RoomEvent.Timeline, handleTimeline);
      mx.removeListener(RoomEvent.Redaction, handleRedaction);
    };
  }, [mx, roomKey, roomIds, threadRootId, wait]);
}
