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

class MetricsService {
  constructor(baseUrl) {
    this.baseUrl = baseUrl || window.location.origin;
    this.socket = null;
    this.subscriptions = new Map();
    this.pendingRequests = new Map();
    window.addEventListener('beforeunload', this.cleanupAll.bind(this));
    window.addEventListener('pagehide', this.cleanupAll.bind(this));

    window.addEventListener('beforeunload', () => {
      this.cleanupAll();
    });
  }

  connectWebSocket() {
    if (this.socket) {
      console.log('WebSocket already exists');
      return;
    }

    console.log('Connecting WebSocket...');
    this.socket = io(`${this.baseUrl.replace('http', 'ws')}/ws`, {
      transports: ['websocket'],
      withCredentials: true,
    });

    this.socket.on('connect', () => {
      console.log('WebSocket connected');
      // Восстанавливаем подписки при переподключении
      this.subscriptions.forEach((_, metricKey) => {
        const [metric, query] = metricKey.split('?');
        const filters = this.parseFiltersFromKey(metricKey);
        this.socket.emit('subscribe-metric', { metric, filters });
      });
    });

    this.socket.on('disconnect', () => {
      console.log('WebSocket disconnected');
      this.socket = null;
    });

    this.socket.on('metrics-data', ({ metric, data, requestId }) => {
      if (requestId && this.pendingRequests.has(requestId)) {
        const { resolve } = this.pendingRequests.get(requestId);
        resolve(data);
        this.pendingRequests.delete(requestId);
        return;
      }

      const callbacks = this.subscriptions.get(metric) || [];
      callbacks.forEach(cb => cb(data));
    });

    this.socket.on('metrics-error', ({ error, requestId }) => {
      if (requestId && this.pendingRequests.has(requestId)) {
        const { reject } = this.pendingRequests.get(requestId);
        reject(new Error(error));
        this.pendingRequests.delete(requestId);
      }
    });
  }

  async fetchMetricsRange(metric, start, end, step = 15, filters = {}) {
    return new Promise((resolve, reject) => {
      this.connectWebSocket();

      const requestId = `range-${Date.now()}`;
      this.pendingRequests.set(requestId, { resolve, reject });

      this.socket.emit('get-metrics', {
        metric,
        start,
        end,
        step,
        filters,
        isRangeQuery: true,
        requestId
      });

      setTimeout(() => {
        if (this.pendingRequests.has(requestId)) {
          reject(new Error('Request timeout'));
          this.pendingRequests.delete(requestId);
        }
      }, 30000);
    });
  }

  subscribeToMetric(metricKey, callback, interval = 5000, filters = {}) {
    this.connectWebSocket();

    if (!this.subscriptions.has(metricKey)) {
      this.subscriptions.set(metricKey, []);
      const [metric] = metricKey.split('?');
      this.socket.emit('subscribe-metric', {
        metric,
        interval,
        filters
      });
    }

    const callbacks = this.subscriptions.get(metricKey);
    callbacks.push(callback);

    return () => {
      this.unsubscribeFromMetric(metricKey, callback);
    };
  }

  unsubscribeFromMetric(metricKey, callback) {
    const callbacks = this.subscriptions.get(metricKey) || [];
    const filtered = callbacks.filter(cb => cb !== callback);

    if (filtered.length === 0) {
      this.subscriptions.delete(metricKey);
      if (this.socket && this.socket.connected) {
        const [metric] = metricKey.split('?');
        this.socket.emit('unsubscribe-metric', { metric });
      }
    } else {
      this.subscriptions.set(metricKey, filtered);
    }
  }

  parseFiltersFromKey(metricKey) {
    const parts = metricKey.split('?');
    if (parts.length < 2) return {};

    return parts[1].split('&').reduce((acc, pair) => {
      const [key, value] = pair.split('=');
      if (key && value) acc[key] = value;
      return acc;
    }, {});
  }

  cleanupAll() {
    if (this.socket && this.socket.connected) {
      this.socket.emit('unsubscribe-all');
    }
    this.subscriptions.clear();
    this.disconnectWebSocket();
  }

  disconnectWebSocket() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }
}

// Создаем экземпляр сервиса
const metricsService = new MetricsService();

export default metricsService;