import ReconnectingWebSocket from 'reconnecting-websocket';
import { getAccessToken } from './storage';

type MessageHandler = (data: any) => void;

class WSClient {
  private ws: ReconnectingWebSocket | null = null;
  private handler = new Set<MessageHandler>();

  /**
   * Kết nối tới WebSocket server.
   * @param url Địa chỉ WebSocket server (có thể là relative path như /mqtt)
   * @param isAuthenticated Có sử dụng token xác thực hay không
   */
  connect(url: string, isAuthenticated: boolean) {
    if (this.ws) return;
    let token = '';
    if (isAuthenticated) {
      token = getAccessToken();
    }
    let wsUrl = url;
    if (url.startsWith('/')) {
      // Relative path, prepend base WebSocket URL
      const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
      wsUrl = `${protocol}//${window.location.host}${url}`;
    }
    wsUrl = isAuthenticated ? `${wsUrl}?token=${token}` : wsUrl;
    this.ws = new ReconnectingWebSocket(wsUrl, [], {
      maxRetries: 10,
      maxReconnectionDelay: 10000,
    });
    this.ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        this.handler.forEach((fn) => fn(data));
      } catch (error) {
        console.error('WS Parse Error: ', error);
      }
    };
  }
  /**
   * Ngắt kết nối WebSocket và giải phóng tài nguyên.
   */
  disconnect() {
    this.ws?.close();
    this.ws = null;
  }

  /**
   * Gửi dữ liệu qua WebSocket.
   * @param data Dữ liệu cần gửi (sẽ được stringify)
   */
  send(data: any) {
    this.ws?.send(JSON.stringify(data));
  }

  /**
   * Đăng ký callback để nhận dữ liệu từ WebSocket.
   * @param cb Hàm callback xử lý dữ liệu nhận được
   * @returns Hàm hủy đăng ký callback
   */
  subscribe(cb: MessageHandler) {
    this.handler.add(cb);
    return () => this.handler.delete(cb);
  }

  /**
   * Kiểm tra trạng thái kết nối WebSocket.
   * @returns true nếu đã kết nối, ngược lại là false
   */
  isConnected() {
    return this.ws?.readyState === WebSocket.OPEN;
  }
}

export const wsClient = new WSClient();
