type MessageHandler = (data: Record<string, unknown>) => void;

class ChatSocket {
  private ws: WebSocket | null = null;
  private handlers: Map<string, MessageHandler[]> = new Map();
  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
  private intentionallyClosed = false;

  connect() {
    this.intentionallyClosed = false;
    const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
    const url = `${protocol}//${location.host}/ws`;
    this.ws = new WebSocket(url);
    this.ws.onopen = () => { this.emit('connected', {}); };
    this.ws.onmessage = (event) => {
      try { const { type, data } = JSON.parse(event.data); this.emit(type, data); } catch {}
    };
    this.ws.onclose = () => {
      if (!this.intentionallyClosed) {
        this.reconnectTimer = setTimeout(() => this.connect(), 3000);
      }
    };
  }

  on(type: string, handler: MessageHandler) {
    if (!this.handlers.has(type)) this.handlers.set(type, []);
    this.handlers.get(type)!.push(handler);
  }

  off(type: string, handler: MessageHandler) {
    const list = this.handlers.get(type);
    if (list) {
      const idx = list.indexOf(handler);
      if (idx !== -1) list.splice(idx, 1);
    }
  }

  send(type: string, data: Record<string, unknown> = {}) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type, ...data }));
    }
  }

  stop() { this.send('stop'); }

  private emit(type: string, data: Record<string, unknown>) {
    (this.handlers.get(type) || []).forEach((h) => h(data));
  }

  disconnect() {
    this.intentionallyClosed = true;
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
    this.reconnectTimer = null;
    this.ws?.close();
    this.ws = null;
  }
}

export const chatSocket = new ChatSocket();
