import { io } from 'socket.io-client';

class WebSocketManager {
  constructor() {
    this.socket = null;
    this.subscribers = new Map();
    this.connectionStatus = 'disconnected';
    this.connectionCallbacks = new Set();
    this.connecting = false;
  }

  connect() {
    if (this.socket?.connected || this.connecting) {
      return this.socket;
    }

    this.connecting = true;

    this.socket = io(`${import.meta.env.VITE_BACK_WS_URL}/api/metrics-ws`, {
      transports: ['websocket'],
      reconnection: true,
      reconnectionAttempts: Infinity,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 5000,
    });

    this.socket.on('connect', () => {
      this.connectionStatus = 'connected';
      this.connecting = false;
      this.notifyConnectionStatus();
    });

    this.socket.on('disconnect', (reason) => {
      this.connectionStatus = 'disconnected';
      this.connecting = false;
      this.notifyConnectionStatus();
      if (reason === 'io server disconnect') this.socket.connect();
    });

    this.socket.on('connect_error', (error) => {
      this.connectionStatus = 'error';
      this.notifyConnectionStatus();
      setTimeout(() => this.socket.connect(), 1000);
    });

    this.socket.on('metrics-data', (response) => {
      const callbacks = this.subscribers.get(response.metric);
      if (callbacks) {
        callbacks.forEach(callback => callback(response.data));
      }
    });

    return this.socket;
  }

  subscribe(metricName, callback) {
    if (!this.socket?.connected) {
      this.connect();
    }

    if (!this.subscribers.has(metricName)) {
      this.subscribers.set(metricName, new Set());
      this.socket.emit('subscribe-metric', {
        metric: metricName,
        isSubscription: true // Флаг для подписки
      });
    }

    this.subscribers.get(metricName).add(callback);

    return () => this.unsubscribe(metricName, callback);
  }

  unsubscribe(metricName, callback) {
    const callbacks = this.subscribers.get(metricName);
    if (callbacks) {
      callbacks.delete(callback);
      if (callbacks.size === 0) {
        this.subscribers.delete(metricName);
        this.socket.emit('unsubscribe-metric', { metric: metricName });
      }
    }
  }

  getMetricsRange(metricName, start, end, step) {
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        reject(new Error('Timeout while waiting for metrics data'));
      }, 10000);

      // Временный обработчик для разового запроса
      const tempHandler = (data) => {
        clearTimeout(timer);
        this.socket.off(`metrics-range-${metricName}`, tempHandler);
        resolve(data);
      };

      this.socket.on(`metrics-range-${metricName}`, tempHandler);
      this.socket.emit('get-metrics', {
        metric: metricName,
        start,
        end,
        step,
        isRangeQuery: true // Флаг для разового запроса
      });
    });
  }

  onConnectionStatusChange(callback) {
    this.connectionCallbacks.add(callback);
    callback(this.connectionStatus);
    return () => this.connectionCallbacks.delete(callback);
  }

  notifyConnectionStatus() {
    this.connectionCallbacks.forEach(callback => callback(this.connectionStatus));
  }
}


export const webSocketManager = new WebSocketManager();