import type { RealtimeChannel } from '@supabase/supabase-js';
import { useCallback, useEffect, useRef, useState } from 'react';

import { supabase } from './supabase';

// Typing events live on a Realtime BROADCAST channel — no DB writes.
// Each typer pings once every 2s while actively typing. Receivers keep a
// per-user timestamp and show the indicator for 4s after the last ping.

const SEND_THROTTLE_MS = 2000;
const RECEIVE_TTL_MS = 4000;

export interface UseTypingChannel {
  typingUserIds: string[];
  notifyTyping: () => void;
  notifyStopTyping: () => void;
}

export function useTypingChannel(
  conversationId: string | undefined,
  myId: string | undefined,
): UseTypingChannel {
  const [typingUserIds, setTypingUserIds] = useState<string[]>([]);
  const channelRef = useRef<RealtimeChannel | null>(null);
  const lastSentRef = useRef(0);
  const receivedRef = useRef<Map<string, number>>(new Map());

  useEffect(() => {
    receivedRef.current = new Map();
    setTypingUserIds([]);

    if (!conversationId || !myId) return;

    const channel = supabase.channel('typing:' + conversationId, {
      config: { broadcast: { self: false } },
    });
    channelRef.current = channel;

    channel.on('broadcast', { event: 'typing' }, (msg) => {
      const payload = msg.payload as { userId?: string; stop?: boolean };
      const uid = payload.userId;
      if (!uid || uid === myId) return;
      if (payload.stop) {
        receivedRef.current.delete(uid);
      } else {
        receivedRef.current.set(uid, Date.now());
      }
      setTypingUserIds(collectRecent(receivedRef.current));
    });

    void channel.subscribe();

    const interval = window.setInterval(() => {
      const active = collectRecent(receivedRef.current);
      setTypingUserIds((prev) => {
        if (prev.length === active.length && prev.every((v, i) => v === active[i])) return prev;
        return active;
      });
    }, 1000);

    return () => {
      window.clearInterval(interval);
      void supabase.removeChannel(channel);
      channelRef.current = null;
    };
  }, [conversationId, myId]);

  const notifyTyping = useCallback(() => {
    const ch = channelRef.current;
    if (!ch || !myId) return;
    const now = Date.now();
    if (now - lastSentRef.current < SEND_THROTTLE_MS) return;
    lastSentRef.current = now;
    void ch.send({ type: 'broadcast', event: 'typing', payload: { userId: myId } });
  }, [myId]);

  const notifyStopTyping = useCallback(() => {
    const ch = channelRef.current;
    if (!ch || !myId) return;
    lastSentRef.current = 0;
    void ch.send({ type: 'broadcast', event: 'typing', payload: { userId: myId, stop: true } });
  }, [myId]);

  return { typingUserIds, notifyTyping, notifyStopTyping };
}

function collectRecent(map: Map<string, number>): string[] {
  const now = Date.now();
  const out: string[] = [];
  for (const [uid, ts] of map) {
    if (now - ts < RECEIVE_TTL_MS) out.push(uid);
    else map.delete(uid);
  }
  return out.sort();
}
