import { useEffect, useState } from 'react';
import { MatrixEvent, Room, RoomEvent, RoomEventHandlerMap, EventStatus } from 'matrix-js-sdk';
import { useMatrixClient } from './useMatrixClient';

export enum MessageDeliveryStatus {
  Sending = 'sending',
  Sent = 'sent',
  Read = 'read',
}

function getDeliveryStatus(
  room: Room,
  mEvent: MatrixEvent,
  myUserId: string
): MessageDeliveryStatus | null {
  if (mEvent.getSender() !== myUserId) return null;

  const { status } = mEvent;
  if (
    status === EventStatus.SENDING ||
    status === EventStatus.ENCRYPTING ||
    status === EventStatus.QUEUED
  ) {
    return MessageDeliveryStatus.Sending;
  }

  if (status === EventStatus.NOT_SENT || status === EventStatus.CANCELLED) return null;

  const eventId = mEvent.getId();
  if (!eventId || !eventId.startsWith('$')) return MessageDeliveryStatus.Sending;

  const members = room.getMembers();
  const otherMembers = members.filter((m) => m.userId !== myUserId && m.membership !== 'invite');

  const hasReader = otherMembers.some((member) => room.hasUserReadEvent(member.userId, eventId));
  if (hasReader) return MessageDeliveryStatus.Read;

  return MessageDeliveryStatus.Sent;
}

export function useMessageStatus(room: Room, mEvent: MatrixEvent): MessageDeliveryStatus | null {
  const mx = useMatrixClient();
  const myUserId = mx.getSafeUserId();
  const isOwnMessage = mEvent.getSender() === myUserId;

  const [status, setStatus] = useState<MessageDeliveryStatus | null>(() =>
    getDeliveryStatus(room, mEvent, myUserId)
  );

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

    setStatus(getDeliveryStatus(room, mEvent, myUserId));

    const handleReceipt: RoomEventHandlerMap[RoomEvent.Receipt] = (_event, r) => {
      if (r.roomId !== room.roomId) return;
      setStatus(getDeliveryStatus(room, mEvent, myUserId));
    };

    const handleLocalEcho: RoomEventHandlerMap[RoomEvent.LocalEchoUpdated] = (event, r) => {
      if (r.roomId !== room.roomId) return;
      if (event.getId() !== mEvent.getId()) return;
      setStatus(getDeliveryStatus(room, mEvent, myUserId));
    };

    room.on(RoomEvent.Receipt, handleReceipt);
    room.on(RoomEvent.LocalEchoUpdated, handleLocalEcho);
    return () => {
      room.removeListener(RoomEvent.Receipt, handleReceipt);
      room.removeListener(RoomEvent.LocalEchoUpdated, handleLocalEcho);
    };
  }, [room, mEvent, myUserId, isOwnMessage]);

  return status;
}
