'use client';

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from 'react';
import { io, type Socket } from 'socket.io-client';
import { useAuth } from '@/lib/hooks/useAuth';
import { accessTokenRefreshedEventName } from '@/lib/auth/accessTokenEvents';
import { notifyTabBadgesChanged } from '@/lib/tabBadgeUtils';
import { notifyPendingConnectionsChanged } from '@/lib/hooks/usePendingConnectionsCount';
import type { UserNotificationItem } from '@/types/notifications';

type RealtimeContextValue = {
  connected: boolean;
  lastNotification: UserNotificationItem | null;
  unreadCount: number | null;
  setUnreadCount: (count: number) => void;
};

const RealtimeContext = createContext<RealtimeContextValue>({
  connected: false,
  lastNotification: null,
  unreadCount: null,
  setUnreadCount: () => undefined,
});

function apiOrigin(): string {
  const base = process.env.NEXT_PUBLIC_API_URL ?? '';
  try {
    return new URL(base).origin;
  } catch {
    return typeof window !== 'undefined' ? window.location.origin : '';
  }
}

function invalidateSidebarBadges(notification?: UserNotificationItem | null) {
  // Sidebar Cases/Tasks/Treatment badges + open tab soft-refetch (via window event).
  notifyTabBadgesChanged();
  // Orgs pending badge/list — only for connection-related inbox events.
  if (
    notification?.type === 'CONNECTION_REQUEST' ||
    notification?.href?.startsWith('/organizations')
  ) {
    notifyPendingConnectionsChanged();
  }
}

export function RealtimeProvider({ children }: { children: ReactNode }) {
  const { user, currentOrganization, isAuthReady } = useAuth();
  const [connected, setConnected] = useState(false);
  const [lastNotification, setLastNotification] = useState<UserNotificationItem | null>(null);
  const [unreadCount, setUnreadCount] = useState<number | null>(null);
  /** Bumped after access-token refresh so the socket reconnects with the new cookie. */
  const [socketEpoch, setSocketEpoch] = useState(0);
  const socketRef = useRef<Socket | null>(null);

  useEffect(() => {
    const onRefreshed = () => setSocketEpoch((n) => n + 1);
    window.addEventListener(accessTokenRefreshedEventName(), onRefreshed);
    return () => window.removeEventListener(accessTokenRefreshedEventName(), onRefreshed);
  }, []);

  useEffect(() => {
    if (!isAuthReady || !user || !currentOrganization?.id) {
      socketRef.current?.disconnect();
      socketRef.current = null;
      setConnected(false);
      return;
    }

    const socket = io(`${apiOrigin()}/realtime`, {
      withCredentials: true,
      transports: ['websocket', 'polling'],
      // Avoid hammering the gateway with an expired cookie; we reconnect after refresh.
      reconnection: true,
      reconnectionAttempts: 5,
      reconnectionDelay: 2000,
    });
    socketRef.current = socket;

    socket.on('connect', () => setConnected(true));
    socket.on('disconnect', () => setConnected(false));
    socket.on('connect_error', () => setConnected(false));
    socket.on('notification.created', (payload: { notification?: UserNotificationItem }) => {
      if (payload?.notification) {
        setLastNotification(payload.notification);
        invalidateSidebarBadges(payload.notification);
      } else {
        invalidateSidebarBadges(null);
      }
    });
    socket.on('notification.unreadCount', (payload: { count?: number }) => {
      if (typeof payload?.count === 'number') {
        setUnreadCount(payload.count);
      }
    });

    return () => {
      socket.disconnect();
      socketRef.current = null;
      setConnected(false);
    };
  }, [isAuthReady, user, currentOrganization?.id, socketEpoch]);

  const setUnreadCountStable = useCallback((count: number) => {
    setUnreadCount(count);
  }, []);

  const value = useMemo(
    () => ({
      connected,
      lastNotification,
      unreadCount,
      setUnreadCount: setUnreadCountStable,
    }),
    [connected, lastNotification, unreadCount, setUnreadCountStable],
  );

  return <RealtimeContext.Provider value={value}>{children}</RealtimeContext.Provider>;
}

export function useRealtime() {
  return useContext(RealtimeContext);
}
