import {
  listGroupDeliveriesForMessages,
  listGroupReadsForMessages,
} from '@chat-app/shared/chat';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { supabase } from './supabase';

export interface GroupReceiptState {
  // message_id → set of user ids who have delivered/read.
  deliveredByMessage: Map<string, Set<string>>;
  readByMessage: Map<string, Set<string>>;
  refresh: () => Promise<void>;
}

// Aggregated delivery + read receipts for group conversations. Returns the
// set of user ids per message; consumers join with conversation members to
// figure out who has not yet acknowledged.
export function useGroupReceipts(
  messageIds: string[],
  selfUserId: string | undefined,
  active: boolean,
): GroupReceiptState {
  const idsKey = useMemo(() => messageIds.join(','), [messageIds]);
  const [deliveredByMessage, setDelivered] = useState<Map<string, Set<string>>>(new Map());
  const [readByMessage, setRead] = useState<Map<string, Set<string>>>(new Map());

  const refresh = useCallback(async () => {
    if (!active || !selfUserId || messageIds.length === 0) {
      setDelivered(new Map());
      setRead(new Map());
      return;
    }
    try {
      const [delivered, read] = await Promise.all([
        listGroupDeliveriesForMessages(supabase, messageIds, selfUserId),
        listGroupReadsForMessages(supabase, messageIds, selfUserId),
      ]);
      setDelivered(toIdSets(delivered));
      setRead(toIdSets(read));
    } catch (err: unknown) {
      console.warn('useGroupReceipts refresh failed', err);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [active, selfUserId, idsKey]);

  useEffect(() => {
    void refresh();
    if (!active || !selfUserId) return;

    // Listen on both tables. RLS already enforces visibility.
    const reads = supabase
      .channel('group-reads:' + selfUserId)
      .on(
        'postgres_changes',
        { event: 'INSERT', schema: 'public', table: 'message_reads' },
        () => {
          void refresh();
        },
      )
      .subscribe();
    const deliveries = supabase
      .channel('group-deliv:' + selfUserId)
      .on(
        'postgres_changes',
        { event: 'INSERT', schema: 'public', table: 'message_deliveries' },
        () => {
          void refresh();
        },
      )
      .subscribe();
    return () => {
      void supabase.removeChannel(reads);
      void supabase.removeChannel(deliveries);
    };
  }, [active, selfUserId, refresh]);

  return { deliveredByMessage, readByMessage, refresh };
}

function toIdSets(input: Map<string, Map<string, string>>): Map<string, Set<string>> {
  const out = new Map<string, Set<string>>();
  for (const [mid, inner] of input) {
    out.set(mid, new Set(inner.keys()));
  }
  return out;
}
