/*
 * WebSocket(STOMP over SockJS) — RenderJob 완료 푸시 구독.
 * 근거: 계약 §6 — 핸드셰이크 GET /ws(SockJS), 브로드캐스트 prefix /topic,
 *   구독 /topic/render/{jobId} → RenderJobDto (design.md §1-4).
 */
import { Client, type IMessage, type StompSubscription } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
import type { RenderJobDto } from './types';

let client: Client | null = null;
let refCount = 0;

const WS_URL = `${(import.meta.env.VITE_API_BASE as string | undefined) ?? ''}/ws`;

function ensureClient(): Client {
  if (client) return client;
  client = new Client({
    // SockJS 팩토리 (계약이 SockJS 핸드셰이크 명시).
    webSocketFactory: () => new SockJS(WS_URL) as unknown as WebSocket,
    reconnectDelay: 4000,
    heartbeatIncoming: 10000,
    heartbeatOutgoing: 10000,
    // 조용한 프로덕션 로깅
    debug: () => {},
  });
  client.activate();
  return client;
}

/**
 * 특정 RenderJob 완료/실패 이벤트를 구독한다.
 * @returns 해제 함수 — 언마운트 시 호출.
 */
export function subscribeRenderJob(
  jobId: string,
  onUpdate: (job: RenderJobDto) => void,
): () => void {
  const c = ensureClient();
  refCount += 1;

  let sub: StompSubscription | null = null;
  const topic = `/topic/render/${jobId}`;

  const doSubscribe = () => {
    sub = c.subscribe(topic, (msg: IMessage) => {
      try {
        onUpdate(JSON.parse(msg.body) as RenderJobDto);
      } catch {
        /* 잘못된 페이로드 무시 */
      }
    });
  };

  if (c.connected) {
    doSubscribe();
  } else {
    // 연결 후 구독
    const prev = c.onConnect;
    c.onConnect = (frame) => {
      prev?.(frame);
      doSubscribe();
    };
  }

  return () => {
    sub?.unsubscribe();
    refCount -= 1;
    if (refCount <= 0 && client) {
      void client.deactivate();
      client = null;
      refCount = 0;
    }
  };
}
