import type { Dispatch, SetStateAction } from 'react';
import { useEffect, useState } from 'react';
import {
  CHAT_CONNECTION,
  diagnoseConnectionFailure,
  getLastReceivedChatEventId,
  handleChatServerEvent,
  persistLastReceivedChatEventId,
  resolveChatWebSocketUrl,
} from './chatUtils';
import { hasRegisteredAccessTokenAccess } from '../tokenAccess';
import type {
  ChatActivityEvent,
  ChatConversationRequest,
  ChatJobEvent,
  ChatMessage,
  ChatRuntimeJobDetail,
  ChatRuntimeSnapshot,
  ChatViewContext,
} from './types';

const DISCONNECT_UI_DELAY_MS = 1500;
const PRESENCE_PING_INTERVAL_MS = 20_000;

type ConnectionState = 'connecting' | 'connected' | 'disconnected';

type UseChatConnectionOptions = {
  sessionId: string;
  currentContext: ChatViewContext;
  setMessages: Dispatch<SetStateAction<ChatMessage[]>>;
  onMessageEvent?: (message: ChatMessage, sessionId: string) => void;
  onJobEvent?: (event: ChatJobEvent, sessionId: string) => void;
  onRuntimeEvent?: (snapshot: ChatRuntimeSnapshot) => void;
  onRuntimeDetailEvent?: (detail: ChatRuntimeJobDetail) => void;
  onActivityEvent?: (event: ChatActivityEvent) => void;
  onRequestEvent?: (request: ChatConversationRequest, sessionId: string) => void;
};

type SharedChatConnectionState = {
  connectionState: ConnectionState;
  connectionErrorDetail: string;
  runtimeSnapshot: ChatRuntimeSnapshot | null;
};

type SharedChatConnection = SharedChatConnectionState & {
  socketRef: { current: WebSocket | null };
  reconnectTimerId: number | null;
  disconnectUiTimerId: number | null;
  connectTimeoutId: number | null;
  sessionId: string;
  currentContext: ChatViewContext | null;
  setMessages: Dispatch<SetStateAction<ChatMessage[]>> | null;
  onMessageEvent?: ((message: ChatMessage, sessionId: string) => void) | undefined;
  onJobEvent?: ((event: ChatJobEvent, sessionId: string) => void) | undefined;
  onRuntimeEvent?: ((snapshot: ChatRuntimeSnapshot) => void) | undefined;
  onRuntimeDetailEvent?: ((detail: ChatRuntimeJobDetail) => void) | undefined;
  onActivityEvent?: ((event: ChatActivityEvent) => void) | undefined;
  onRequestEvent?: ((request: ChatConversationRequest, sessionId: string) => void) | undefined;
  lastEventId: number;
  websocketUrl: string;
  subscribers: Set<() => void>;
  pingSubscriberCount: number;
  consumerCount: number;
  pingIntervalId: number | null;
  visibilityHandlerInstalled: boolean;
  pageHideHandlerInstalled: boolean;
  beforeUnloadHandlerInstalled: boolean;
  unloadHandlerInstalled: boolean;
  pageShowHandlerInstalled: boolean;
  focusHandlerInstalled: boolean;
  blurHandlerInstalled: boolean;
  onlineHandlerInstalled: boolean;
  hasConnectedOnce: boolean;
  suppressDisconnectNotification: boolean;
  lastBackgroundAt: number | null;
};

const sharedChatConnection: SharedChatConnection = {
  connectionState: 'connecting',
  connectionErrorDetail: '',
  runtimeSnapshot: null,
  socketRef: { current: null },
  reconnectTimerId: null,
  disconnectUiTimerId: null,
  connectTimeoutId: null,
  sessionId: '',
  currentContext: null,
  setMessages: null,
  onMessageEvent: undefined,
  onJobEvent: undefined,
  onRuntimeEvent: undefined,
  onRuntimeDetailEvent: undefined,
  onActivityEvent: undefined,
  onRequestEvent: undefined,
  lastEventId: 0,
  websocketUrl: '',
  subscribers: new Set(),
  pingSubscriberCount: 0,
  consumerCount: 0,
  pingIntervalId: null,
  visibilityHandlerInstalled: false,
  pageHideHandlerInstalled: false,
  beforeUnloadHandlerInstalled: false,
  unloadHandlerInstalled: false,
  pageShowHandlerInstalled: false,
  focusHandlerInstalled: false,
  blurHandlerInstalled: false,
  onlineHandlerInstalled: false,
  hasConnectedOnce: false,
  suppressDisconnectNotification: false,
  lastBackgroundAt: null,
};

function emitSharedState() {
  sharedChatConnection.subscribers.forEach((listener) => {
    listener();
  });
}

function getSnapshot(): SharedChatConnectionState {
  return {
    connectionState: sharedChatConnection.connectionState,
    connectionErrorDetail: sharedChatConnection.connectionErrorDetail,
    runtimeSnapshot: sharedChatConnection.runtimeSnapshot,
  };
}

export function getChatConnectionSnapshot() {
  return getSnapshot();
}

export function subscribeChatConnection(listener: () => void) {
  sharedChatConnection.subscribers.add(listener);

  return () => {
    sharedChatConnection.subscribers.delete(listener);
  };
}

export function getSharedChatRuntimeSnapshot() {
  return sharedChatConnection.runtimeSnapshot;
}

export function setSharedChatRuntimeSnapshot(snapshot: ChatRuntimeSnapshot | null) {
  if (sharedChatConnection.runtimeSnapshot === snapshot) {
    return;
  }

  sharedChatConnection.runtimeSnapshot = snapshot;
  emitSharedState();
}

function setSharedConnectionState(nextState: ConnectionState) {
  if (sharedChatConnection.connectionState === nextState) {
    return;
  }

  sharedChatConnection.connectionState = nextState;
  emitSharedState();
}

function setSharedConnectionError(detail: string) {
  if (sharedChatConnection.connectionErrorDetail === detail) {
    return;
  }

  sharedChatConnection.connectionErrorDetail = detail;
  emitSharedState();
}

function clearReconnectTimer() {
  if (sharedChatConnection.reconnectTimerId !== null) {
    window.clearTimeout(sharedChatConnection.reconnectTimerId);
    sharedChatConnection.reconnectTimerId = null;
  }
}

function clearDisconnectUiTimer() {
  if (sharedChatConnection.disconnectUiTimerId !== null) {
    window.clearTimeout(sharedChatConnection.disconnectUiTimerId);
    sharedChatConnection.disconnectUiTimerId = null;
  }
}

function clearConnectTimeout() {
  if (sharedChatConnection.connectTimeoutId !== null) {
    window.clearTimeout(sharedChatConnection.connectTimeoutId);
    sharedChatConnection.connectTimeoutId = null;
  }
}

function sendContextUpdate(context: ChatViewContext | null = sharedChatConnection.currentContext) {
  const socket = sharedChatConnection.socketRef.current;

  if (!socket || socket.readyState !== WebSocket.OPEN || !context) {
    return;
  }

  const liveVisibilityState =
    typeof document !== 'undefined' && document.visibilityState === 'hidden' ? 'hidden' : 'visible';
  const liveFocusState =
    typeof document !== 'undefined' && typeof document.hasFocus === 'function' && !document.hasFocus()
      ? 'blurred'
      : 'focused';
  const livePageUrl = typeof window !== 'undefined' ? window.location.href : context.pageUrl;
  const liveAppOrigin = typeof window !== 'undefined' ? window.location.origin : context.appOrigin ?? '';
  const liveAppDomain = typeof window !== 'undefined' ? window.location.hostname : context.appDomain ?? '';

  socket.send(
    JSON.stringify({
      type: 'context:update',
      payload: {
        pageId: context.pageId,
        pageTitle: context.pageTitle,
        topMenu: context.topMenu,
        focusedComponentId: context.focusedComponentId,
        pageUrl: livePageUrl,
        appOrigin: liveAppOrigin,
        appDomain: liveAppDomain,
        isStandaloneMode: context.isStandaloneMode,
        pageVisibilityState: liveVisibilityState,
        pageFocusState: liveFocusState,
        codexModel: context.codexModel ?? null,
        codexParticipants: context.codexParticipants ?? [],
        chatTypeId: context.chatTypeId,
        chatTypeLabel: context.chatTypeLabel,
        chatTypeDescription: context.chatTypeDescription,
        chatTypeBaseDescription: context.chatTypeBaseDescription,
        defaultContextIds: context.defaultContextIds,
        defaultContexts: context.defaultContexts,
        customContextTitle: context.customContextTitle,
        customContextContent: context.customContextContent,
      },
    }),
  );
}

function sendPresencePing() {
  const socket = sharedChatConnection.socketRef.current;

  if (!socket || socket.readyState !== WebSocket.OPEN) {
    return;
  }

  socket.send(
    JSON.stringify({
      type: 'presence:ping',
      payload: {
        at: Date.now(),
      },
    }),
  );
}

function sendImmediateHiddenContextUpdate() {
  const context = sharedChatConnection.currentContext;
  const socket = sharedChatConnection.socketRef.current;

  if (!socket || socket.readyState !== WebSocket.OPEN || !context) {
    return;
  }

  const livePageUrl = typeof window !== 'undefined' ? window.location.href : context.pageUrl;
  const liveAppOrigin = typeof window !== 'undefined' ? window.location.origin : context.appOrigin ?? '';
  const liveAppDomain = typeof window !== 'undefined' ? window.location.hostname : context.appDomain ?? '';

  socket.send(
    JSON.stringify({
      type: 'context:update',
      payload: {
        pageId: context.pageId,
        pageTitle: context.pageTitle,
        topMenu: context.topMenu,
        focusedComponentId: context.focusedComponentId,
        pageUrl: livePageUrl,
        appOrigin: liveAppOrigin,
        appDomain: liveAppDomain,
        isStandaloneMode: context.isStandaloneMode,
        pageVisibilityState: 'hidden',
        pageFocusState: 'blurred',
        codexModel: context.codexModel ?? null,
        codexParticipants: context.codexParticipants ?? [],
        chatTypeId: context.chatTypeId,
        chatTypeLabel: context.chatTypeLabel,
        chatTypeDescription: context.chatTypeDescription,
        chatTypeBaseDescription: context.chatTypeBaseDescription,
        defaultContextIds: context.defaultContextIds,
        defaultContexts: context.defaultContexts,
        customContextTitle: context.customContextTitle,
        customContextContent: context.customContextContent,
      },
    }),
  );
}

function closeSharedSocketForPageExit() {
  clearReconnectTimer();
  clearConnectTimeout();
  clearDisconnectUiTimer();

  const socket = sharedChatConnection.socketRef.current;

  if (!socket) {
    return;
  }

  sharedChatConnection.suppressDisconnectNotification = true;
  sharedChatConnection.socketRef.current = null;
  socket.close(1000, 'page-exit');
}

function ensureSharedSocket() {
  const socket = sharedChatConnection.socketRef.current;

  if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
    return;
  }

  connectSharedSocket();
}

function sendEventReceived(eventId: number) {
  const socket = sharedChatConnection.socketRef.current;

  if (!socket || socket.readyState !== WebSocket.OPEN || !Number.isFinite(eventId) || eventId <= 0) {
    return;
  }

  socket.send(
    JSON.stringify({
      type: 'event:received',
      payload: {
        eventId,
      },
    }),
  );
}

function stopPresenceMonitoring() {
  if (sharedChatConnection.pingIntervalId !== null) {
    window.clearInterval(sharedChatConnection.pingIntervalId);
    sharedChatConnection.pingIntervalId = null;
  }

  if (sharedChatConnection.visibilityHandlerInstalled) {
    document.removeEventListener('visibilitychange', handleVisibilityChange);
    sharedChatConnection.visibilityHandlerInstalled = false;
  }

  if (sharedChatConnection.pageHideHandlerInstalled) {
    window.removeEventListener('pagehide', handlePageHide);
    sharedChatConnection.pageHideHandlerInstalled = false;
  }

  if (sharedChatConnection.beforeUnloadHandlerInstalled) {
    window.removeEventListener('beforeunload', handleBeforeUnload);
    sharedChatConnection.beforeUnloadHandlerInstalled = false;
  }

  if (sharedChatConnection.unloadHandlerInstalled) {
    window.removeEventListener('unload', handleWindowUnload);
    sharedChatConnection.unloadHandlerInstalled = false;
  }

  if (sharedChatConnection.pageShowHandlerInstalled) {
    window.removeEventListener('pageshow', handlePageShow);
    sharedChatConnection.pageShowHandlerInstalled = false;
  }

  if (sharedChatConnection.focusHandlerInstalled) {
    window.removeEventListener('focus', handleWindowFocus);
    sharedChatConnection.focusHandlerInstalled = false;
  }

  if (sharedChatConnection.blurHandlerInstalled) {
    window.removeEventListener('blur', handleWindowBlur);
    sharedChatConnection.blurHandlerInstalled = false;
  }

  if (sharedChatConnection.onlineHandlerInstalled) {
    window.removeEventListener('online', handleWindowOnline);
    sharedChatConnection.onlineHandlerInstalled = false;
  }
}

function handleVisibilityChange() {
  if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {
    sharedChatConnection.lastBackgroundAt = Date.now();
    sendContextUpdate(sharedChatConnection.currentContext);
    sendPresencePing();
    return;
  }

  ensureSharedSocket();
  sendPresencePing();
  sendContextUpdate(sharedChatConnection.currentContext);
}

function handlePageShow() {
  ensureSharedSocket();
  sendPresencePing();
  sendContextUpdate(sharedChatConnection.currentContext);
}

function handlePageHide() {
  sharedChatConnection.lastBackgroundAt = Date.now();
  sendImmediateHiddenContextUpdate();
  sendPresencePing();
  closeSharedSocketForPageExit();
}

function handleBeforeUnload() {
  handlePageHide();
}

function handleWindowUnload() {
  handlePageHide();
}

function handleWindowFocus() {
  ensureSharedSocket();
  sendPresencePing();
  sendContextUpdate(sharedChatConnection.currentContext);
}

function handleWindowBlur() {
  sendContextUpdate(sharedChatConnection.currentContext);
  sendPresencePing();
}

function handleWindowOnline() {
  ensureSharedSocket();
}

function startPresenceMonitoring() {
  if (sharedChatConnection.pingSubscriberCount <= 0 || sharedChatConnection.connectionState !== 'connected') {
    stopPresenceMonitoring();
    return;
  }

  sharedChatConnection.lastBackgroundAt = null;
  sendPresencePing();

  if (sharedChatConnection.pingIntervalId === null) {
    sharedChatConnection.pingIntervalId = window.setInterval(() => {
      sendPresencePing();
    }, PRESENCE_PING_INTERVAL_MS);
  }

  if (!sharedChatConnection.visibilityHandlerInstalled) {
    document.addEventListener('visibilitychange', handleVisibilityChange);
    sharedChatConnection.visibilityHandlerInstalled = true;
  }

  if (!sharedChatConnection.pageShowHandlerInstalled) {
    window.addEventListener('pageshow', handlePageShow);
    sharedChatConnection.pageShowHandlerInstalled = true;
  }

  if (!sharedChatConnection.pageHideHandlerInstalled) {
    window.addEventListener('pagehide', handlePageHide);
    sharedChatConnection.pageHideHandlerInstalled = true;
  }

  if (!sharedChatConnection.beforeUnloadHandlerInstalled) {
    window.addEventListener('beforeunload', handleBeforeUnload);
    sharedChatConnection.beforeUnloadHandlerInstalled = true;
  }

  if (!sharedChatConnection.unloadHandlerInstalled) {
    window.addEventListener('unload', handleWindowUnload);
    sharedChatConnection.unloadHandlerInstalled = true;
  }

  if (!sharedChatConnection.focusHandlerInstalled) {
    window.addEventListener('focus', handleWindowFocus);
    sharedChatConnection.focusHandlerInstalled = true;
  }

  if (!sharedChatConnection.blurHandlerInstalled) {
    window.addEventListener('blur', handleWindowBlur);
    sharedChatConnection.blurHandlerInstalled = true;
  }

  if (!sharedChatConnection.onlineHandlerInstalled) {
    window.addEventListener('online', handleWindowOnline);
    sharedChatConnection.onlineHandlerInstalled = true;
  }
}

function scheduleReconnect() {
  if (sharedChatConnection.reconnectTimerId !== null || !sharedChatConnection.sessionId) {
    return;
  }

  sharedChatConnection.reconnectTimerId = window.setTimeout(() => {
    sharedChatConnection.reconnectTimerId = null;
    connectSharedSocket();
  }, CHAT_CONNECTION.reconnectDelayMs);
}

function handleSharedDisconnect(message?: string, detail?: string) {
  setSharedConnectionError(detail ?? '');
  clearDisconnectUiTimer();

  if (sharedChatConnection.connectionState !== 'connected') {
    setSharedConnectionState('disconnected');
  } else {
    sharedChatConnection.disconnectUiTimerId = window.setTimeout(() => {
      sharedChatConnection.disconnectUiTimerId = null;

      if (sharedChatConnection.socketRef.current?.readyState === WebSocket.OPEN) {
        return;
      }

      setSharedConnectionState('disconnected');
    }, DISCONNECT_UI_DELAY_MS);
  }

  if (message) {
    scheduleReconnect();
  }
}

function disconnectSharedSocket() {
  clearReconnectTimer();
  clearConnectTimeout();
  clearDisconnectUiTimer();
  stopPresenceMonitoring();

  const socket = sharedChatConnection.socketRef.current;
  sharedChatConnection.suppressDisconnectNotification = true;
  sharedChatConnection.socketRef.current = null;
  socket?.close();
}

function releaseSharedConnectionConsumer() {
  sharedChatConnection.consumerCount = Math.max(0, sharedChatConnection.consumerCount - 1);

  if (sharedChatConnection.consumerCount > 0) {
    return;
  }

  sharedChatConnection.currentContext = null;
  sharedChatConnection.setMessages = null;
  sharedChatConnection.onMessageEvent = undefined;
  sharedChatConnection.onJobEvent = undefined;
  sharedChatConnection.onRuntimeEvent = undefined;
  sharedChatConnection.onRuntimeDetailEvent = undefined;
  setSharedChatRuntimeSnapshot(null);
  disconnectSharedSocket();
  setSharedConnectionError('');
  setSharedConnectionState('disconnected');
}

function connectSharedSocket() {
  if (!sharedChatConnection.sessionId || !sharedChatConnection.setMessages) {
    return;
  }

  if (!hasRegisteredAccessTokenAccess()) {
    clearReconnectTimer();
    clearConnectTimeout();
    clearDisconnectUiTimer();
    stopPresenceMonitoring();
    setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결을 시작하지 않았습니다.');
    setSharedConnectionState('disconnected');
    return;
  }

  const currentSocket = sharedChatConnection.socketRef.current;

  if (currentSocket && (currentSocket.readyState === WebSocket.OPEN || currentSocket.readyState === WebSocket.CONNECTING)) {
    return;
  }

  clearReconnectTimer();
  clearConnectTimeout();
  clearDisconnectUiTimer();

  if (sharedChatConnection.connectionState !== 'connected') {
    setSharedConnectionState('connecting');
  }

  sharedChatConnection.websocketUrl = resolveChatWebSocketUrl(sharedChatConnection.sessionId, sharedChatConnection.lastEventId);

  let socket: WebSocket;

  try {
    socket = new WebSocket(sharedChatConnection.websocketUrl);
  } catch {
    handleSharedDisconnect(
      `워크서버 WebSocket 주소가 올바르지 않습니다. 대상: ${sharedChatConnection.websocketUrl || '/ws/chat'} 자동으로 다시 연결합니다.`,
      'WebSocket 객체를 생성하지 못했습니다. 대상 주소 형식과 환경변수를 확인해 주세요.',
    );
    return;
  }

  sharedChatConnection.socketRef.current = socket;
  sharedChatConnection.suppressDisconnectNotification = false;
  let disconnectHandled = false;

  const reportDisconnect = (message?: string, closeEvent?: CloseEvent) => {
    if (disconnectHandled) {
      return;
    }

    disconnectHandled = true;

    const wasSuppressed = sharedChatConnection.suppressDisconnectNotification;

    if (sharedChatConnection.socketRef.current === socket) {
      sharedChatConnection.socketRef.current = null;
    }

    sharedChatConnection.suppressDisconnectNotification = false;

    if (wasSuppressed) {
      setSharedConnectionError('');
      return;
    }

    if (closeEvent?.code === 1008) {
      clearReconnectTimer();
      setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결이 차단되었습니다.');
      setSharedConnectionState('disconnected');
      return;
    }

    if (closeEvent?.code === 1000 && !message) {
      setSharedConnectionError('');
      handleSharedDisconnect();
      scheduleReconnect();
      return;
    }

    void diagnoseConnectionFailure(sharedChatConnection.websocketUrl, closeEvent).then((detail) => {
      handleSharedDisconnect(message, detail);
    });
  };

  sharedChatConnection.connectTimeoutId = window.setTimeout(() => {
    if (sharedChatConnection.socketRef.current !== socket || socket.readyState === WebSocket.OPEN) {
      return;
    }

    sharedChatConnection.socketRef.current = null;
    socket.close();
    reportDisconnect(
      `워크서버 연결 시간이 초과되었습니다. 대상: ${sharedChatConnection.websocketUrl || '/ws/chat'} 자동으로 다시 연결합니다.`,
    );
  }, CHAT_CONNECTION.connectTimeoutMs);

  socket.addEventListener('open', () => {
    clearConnectTimeout();
    clearDisconnectUiTimer();
    sharedChatConnection.hasConnectedOnce = true;
    sharedChatConnection.suppressDisconnectNotification = false;
    setSharedConnectionState('connected');
    setSharedConnectionError('');
    sendContextUpdate(sharedChatConnection.currentContext);
    startPresenceMonitoring();
  });

  socket.addEventListener('message', (event) => {
    const setMessages = sharedChatConnection.setMessages;

    if (!setMessages) {
      return;
    }

    void handleChatServerEvent({
      eventData: String(event.data),
      currentPageUrl: sharedChatConnection.currentContext?.pageUrl ?? '',
      expectedSessionId: sharedChatConnection.sessionId,
      setMessages,
      onMessageEvent: sharedChatConnection.onMessageEvent,
      onJobEvent: sharedChatConnection.onJobEvent,
      onRuntimeEvent: sharedChatConnection.onRuntimeEvent,
      onRuntimeDetailEvent: sharedChatConnection.onRuntimeDetailEvent,
      onActivityEvent: sharedChatConnection.onActivityEvent,
      onRequestEvent: sharedChatConnection.onRequestEvent,
      onEventReceived: (eventId) => {
        sharedChatConnection.lastEventId = eventId;
        persistLastReceivedChatEventId(sharedChatConnection.sessionId, eventId);
        sendEventReceived(eventId);
      },
    });

    try {
      const parsedEvent = JSON.parse(String(event.data)) as ChatRuntimeEventEnvelope | null;

      if (parsedEvent?.type === 'chat:runtime') {
        setSharedChatRuntimeSnapshot(parsedEvent.payload);
      }
    } catch {
      // ignore malformed payloads here; detailed parsing is already handled downstream
    }
  });

  socket.addEventListener('close', (event) => {
    clearConnectTimeout();
    stopPresenceMonitoring();
    reportDisconnect(
      event.code === 1000 ? undefined : '워크서버 연결이 끊어졌습니다. 자동으로 다시 연결합니다.',
      event,
    );
  });

  socket.addEventListener('error', () => {
    clearConnectTimeout();
    stopPresenceMonitoring();
    reportDisconnect('워크서버 WebSocket 연결에 실패했습니다. 자동으로 다시 연결합니다.');
  });
}

type ChatRuntimeEventEnvelope = {
  type: 'chat:runtime';
  payload: ChatRuntimeSnapshot;
};

function ensureSharedConnection(options: UseChatConnectionOptions) {
  const sessionChanged = sharedChatConnection.sessionId !== options.sessionId;

  sharedChatConnection.currentContext = options.currentContext;
  sharedChatConnection.setMessages = options.setMessages;
  sharedChatConnection.onMessageEvent = options.onMessageEvent;
  sharedChatConnection.onJobEvent = options.onJobEvent;
  sharedChatConnection.onRuntimeEvent = options.onRuntimeEvent;
  sharedChatConnection.onRuntimeDetailEvent = options.onRuntimeDetailEvent;
  sharedChatConnection.onActivityEvent = options.onActivityEvent;
  sharedChatConnection.onRequestEvent = options.onRequestEvent;

  if (sessionChanged) {
    sharedChatConnection.sessionId = options.sessionId;
    sharedChatConnection.lastEventId = getLastReceivedChatEventId(options.sessionId);
    sharedChatConnection.hasConnectedOnce = false;
    disconnectSharedSocket();
  }

  connectSharedSocket();
}

export function useChatConnection({
  sessionId,
  currentContext,
  setMessages,
  onMessageEvent,
  onJobEvent,
  onRuntimeEvent,
  onRuntimeDetailEvent,
  onActivityEvent,
  onRequestEvent,
}: UseChatConnectionOptions) {
  const [snapshot, setSnapshot] = useState<SharedChatConnectionState>(() => getSnapshot());

  useEffect(() => {
    sharedChatConnection.consumerCount += 1;

    return () => {
      releaseSharedConnectionConsumer();
    };
  }, []);

  useEffect(() => {
    const handleSnapshotChange = () => {
      setSnapshot(getSnapshot());
    };

    const unsubscribe = subscribeChatConnection(handleSnapshotChange);
    ensureSharedConnection({
      sessionId,
      currentContext,
      setMessages,
      onMessageEvent,
      onJobEvent,
      onRuntimeEvent,
      onRuntimeDetailEvent,
      onActivityEvent,
      onRequestEvent,
    });
    handleSnapshotChange();

    return () => {
      unsubscribe();
    };
  }, [sessionId, setMessages]);

  useEffect(() => {
    sharedChatConnection.currentContext = currentContext;
    sendContextUpdate(currentContext);
  }, [currentContext]);

  useEffect(() => {
    sharedChatConnection.setMessages = setMessages;
    sharedChatConnection.onMessageEvent = onMessageEvent;
    sharedChatConnection.onJobEvent = onJobEvent;
    sharedChatConnection.onRuntimeEvent = onRuntimeEvent;
    sharedChatConnection.onRuntimeDetailEvent = onRuntimeDetailEvent;
    sharedChatConnection.onActivityEvent = onActivityEvent;
    sharedChatConnection.onRequestEvent = onRequestEvent;
  }, [onActivityEvent, onJobEvent, onMessageEvent, onRequestEvent, onRuntimeDetailEvent, onRuntimeEvent, setMessages]);

  useEffect(() => {
    sharedChatConnection.pingSubscriberCount += 1;
    startPresenceMonitoring();

    return () => {
      sharedChatConnection.pingSubscriberCount = Math.max(0, sharedChatConnection.pingSubscriberCount - 1);

      if (sharedChatConnection.pingSubscriberCount === 0) {
        stopPresenceMonitoring();
      }
    };
  }, []);

  return {
    connectionState: snapshot.connectionState,
    connectionErrorDetail: snapshot.connectionErrorDetail,
    socketRef: sharedChatConnection.socketRef,
  };
}
