import { createContext, useContext, useEffect, useRef, useState } from 'react';
import type { RealtimeChannel } from '@supabase/supabase-js';
import { supabase } from '../lib/supabase';

type OnlinePresenceContext = {
  onlineUserIds: Set<string>;
  isOnline: (userId: string) => boolean;
};

export const OnlinePresenceContext = createContext<OnlinePresenceContext>({
  onlineUserIds: new Set(),
  isOnline: () => false,
});

export function useOnlineUsers(): OnlinePresenceContext {
  return useContext(OnlinePresenceContext);
}

let sharedChannel: RealtimeChannel | null = null;
let subscriberCount = 0;
let onlineUserIds: Set<string> = new Set();
const listeners = new Set<(ids: Set<string>) => void>();

// Offline-Debounce: Presence-Sync kann kurz aussetzen (Reconnect, Sync-Timing).
// Ohne Grace-Period springt der DM-Header dann sofort von „Online" auf „zuletzt
// online vor X" und zurück → sichtbares Flackern. Online-Werden ist sofort,
// Offline-Werden erst nach OFFLINE_GRACE_MS, falls der User bis dahin nicht
// wieder in der Presence auftaucht.
const OFFLINE_GRACE_MS = 12_000;
const pendingRemoval = new Map<string, ReturnType<typeof setTimeout>>();

function clearPendingRemovals() {
  pendingRemoval.forEach((timer) => clearTimeout(timer));
  pendingRemoval.clear();
}

function notify() {
  const snapshot = new Set(onlineUserIds);
  listeners.forEach((fn) => fn(snapshot));
}

function applyRawPresence(rawKeys: string[]) {
  const raw = new Set(rawKeys);
  let changed = false;

  // Anwesende: sofort online + jede geplante Entfernung abbrechen.
  for (const id of raw) {
    const pending = pendingRemoval.get(id);
    if (pending) {
      clearTimeout(pending);
      pendingRemoval.delete(id);
    }
    if (!onlineUserIds.has(id)) {
      onlineUserIds.add(id);
      changed = true;
    }
  }

  // Verschwundene: erst nach Grace-Period entfernen (nicht sofort).
  for (const id of onlineUserIds) {
    if (!raw.has(id) && !pendingRemoval.has(id)) {
      const timer = setTimeout(() => {
        onlineUserIds.delete(id);
        pendingRemoval.delete(id);
        notify();
      }, OFFLINE_GRACE_MS);
      pendingRemoval.set(id, timer);
    }
  }

  if (changed) notify();
}

function ensureChannel(currentUserId: string) {
  if (sharedChannel) return;

  const ch = supabase.channel('presence:online', {
    config: { presence: { key: currentUserId } },
  });
  sharedChannel = ch;

  ch
    .on('presence', { event: 'sync' }, () => {
      const state = ch.presenceState();
      applyRawPresence(Object.keys(state));
    })
    .subscribe(async (status: string) => {
      if (status === 'SUBSCRIBED') {
        await ch.track({ userId: currentUserId, online_at: new Date().toISOString() });
      }
    });
}

function teardownChannel() {
  if (!sharedChannel) return;
  clearPendingRemovals();
  sharedChannel.untrack().catch(() => {});
  supabase.removeChannel(sharedChannel);
  sharedChannel = null;
  onlineUserIds = new Set();
  notify();
}

export function untrackSelf() {
  sharedChannel?.untrack().catch(() => {});
}

export function retrackSelf(currentUserId: string) {
  sharedChannel
    ?.track({ userId: currentUserId, online_at: new Date().toISOString() })
    .catch(() => {});
}

export function useOnlinePresenceNode(currentUserId: string | null | undefined) {
  const [ids, setIds] = useState<Set<string>>(new Set(onlineUserIds));

  useEffect(() => {
    if (!currentUserId) return;

    subscriberCount++;
    ensureChannel(currentUserId);

    const listener = (next: Set<string>) => setIds(next);
    listeners.add(listener);

    return () => {
      listeners.delete(listener);
      subscriberCount--;
      if (subscriberCount <= 0) {
        subscriberCount = 0;
        teardownChannel();
      }
    };
  }, [currentUserId]);

  return ids;
}
