/**
 * 차트·실시간 전략용 STOMP 단일 연결 (SockJS 1회).
 * 훅마다 StompClient 를 만들면 "closed before connection is established" 가 반복된다.
 */
import { Client as StompClient, IMessage, StompSubscription } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
import { getStompSockJsUrl } from './backendApi';

type MessageHandler = (msg: IMessage) => void;

interface TopicEntry {
  handlers: Set<MessageHandler>;
  stompSub?: StompSubscription;
}

let client: StompClient | null = null;
let connected = false;
const topics = new Map<string, TopicEntry>();
let disconnectTimer: ReturnType<typeof setTimeout> | null = null;

function handlerCount(): number {
  let n = 0;
  for (const entry of topics.values()) n += entry.handlers.size;
  return n;
}

function cancelPendingDisconnect(): void {
  if (disconnectTimer) {
    clearTimeout(disconnectTimer);
    disconnectTimer = null;
  }
}

function resubscribeAll(): void {
  if (!client?.connected) return;
  for (const [topic, entry] of topics) {
    if (entry.handlers.size === 0) continue;
    entry.stompSub?.unsubscribe();
    entry.stompSub = client.subscribe(topic, msg => {
      entry.handlers.forEach(h => h(msg));
    });
  }
}

function getOrCreateClient(): StompClient {
  if (client) return client;

  client = new StompClient({
    webSocketFactory: () => new SockJS(getStompSockJsUrl()),
    reconnectDelay: 5000,
    heartbeatIncoming: 10_000,
    heartbeatOutgoing: 10_000,
    onConnect: () => {
      connected = true;
      cancelPendingDisconnect();
      resubscribeAll();
    },
    onDisconnect: () => {
      connected = false;
      for (const entry of topics.values()) {
        entry.stompSub = undefined;
      }
    },
    onStompError: frame => {
      console.warn('[stompChartBroker] STOMP error', frame.headers?.message ?? frame);
    },
    onWebSocketError: ev => {
      console.warn('[stompChartBroker] WebSocket error', getStompSockJsUrl(), ev);
    },
    onWebSocketClose: () => {
      connected = false;
      for (const entry of topics.values()) {
        entry.stompSub = undefined;
      }
    },
  });
  return client;
}

function ensureActive(): void {
  cancelPendingDisconnect();
  const c = getOrCreateClient();
  if (!c.active) c.activate();
}

function scheduleDisconnect(): void {
  cancelPendingDisconnect();
  disconnectTimer = setTimeout(() => {
    disconnectTimer = null;
    if (handlerCount() > 0) return;
    if (!client?.active) return;
    // 연결 수립 중 deactivate 하면 "closed before connection is established" 발생
    if (!connected) {
      scheduleDisconnect();
      return;
    }
    client.deactivate();
    connected = false;
  }, 5000);
}

/**
 * STOMP 토픽 구독 (참조 카운트). 반환 함수로 해제.
 */
export function subscribeStompTopic(topic: string, handler: MessageHandler): () => void {
  let entry = topics.get(topic);
  if (!entry) {
    entry = { handlers: new Set() };
    topics.set(topic, entry);
  }
  entry.handlers.add(handler);

  if (connected && client && entry.handlers.size === 1) {
    entry.stompSub = client.subscribe(topic, msg => {
      entry!.handlers.forEach(h => h(msg));
    });
  }

  ensureActive();

  return () => {
    const e = topics.get(topic);
    if (!e) return;
    e.handlers.delete(handler);
    if (e.handlers.size === 0) {
      e.stompSub?.unsubscribe();
      topics.delete(topic);
      scheduleDisconnect();
    }
  };
}

export function isStompChartConnected(): boolean {
  return connected && Boolean(client?.connected);
}
