const clientActions = {};
// Объект для хранения ограничителей по каждому клиенту
const limiters = {}; 
// Домен
const allowedOrigin = 'https://gpt.loc'; 

wss.on('connection', (ws, req) => {
	//console.log('Новое соединение:', req.socket.remoteAddress);
	const urlParams = new URLSearchParams(req.url.split('?')[1]);
    const userId = urlParams.get('token'); // Получаем UUID из URL
    const userIP = req.socket.remoteAddress;
	//console.log("IP-адрес - ", userIP, "Время подключения - ", new Date().toISOString());
	const userHeaders = req.headers;

	// Проверяем Origin
    if (!checkOrigin(userHeaders.origin, ws)) {
        return; // Если отклонено, выходим из обработчика
    }

	// Если количество подключений с этого IP превышает лимит, закрываем соединение
    if (ipConnectionCount[userIP] >= MAX_CONNECTIONS_PER_IP) {
        console.log(`Превышен лимит подключений для IP: ${userIP}, отказано в соединении.`);
		stats.totalIPBlocks++;
        ws.close(); // Закрываем соединение
        return; // Выходим из обработчика 
    }
	
	// Увеличиваем счетчик подключений
    currentConnectedClients++;
	
	// Обновляем максимальное количество клиентов, если текущее количество больше 
	if (currentConnectedClients > stats.maxConnectedClients) {
        stats.maxConnectedClients = currentConnectedClients;
        console.log(`Максимальное количество подключенных клиентов: ${stats.maxConnectedClients}`);
    }
	
    // Увеличиваем счетчик подключений для этого IP
    ipConnectionCount[userIP] = (ipConnectionCount[userIP] || 0) + 1;
	
	// Увеличиваем общий счетчик подключений
    stats.totalConnections++;
	
	console.log('Client connected with user ID:', userId, ' and IP:', userIP);
	
	// Инициализируем состояние клиента, если его еще нет
    if (!clientActions[userId]) {
		clientActions[userId] = {
			actions: [], // Массив для хранения действий 
			connected: true, // Статус подключения
			lastRequestTime: new Date(), // Добавляем время последнего запроса 
			ws: ws,
			delay: 1000,                // Задержка по умолчанию, например, 1000 мс
			responseTimes: []			// массив для хранения временных меток (начало и конец 3-х ответов)
        };
	}
	else {
        // обновляем ws при переподключении
        clientActions[userId].connected = true; 
        clientActions[userId].ws = ws;
        // вы можете обновить другие параметры здесь
    }
	
	if (!limiters[userId]) { 
	  // Создаем экземпляр Bottleneck для каждого клиента (ограничение на количество запросов по Id)
		limiters[userId] = new Bottleneck({
            maxConcurrent: 1, // Максимальное количество одновременных задач
            minTime: 1000, // Минимальное время между запросами в миллисекундах
		    highWater: 0 // Не разрешать в очереди больше 0 запросов (игнорировать)
        });
    }
	
    ws.on('message', (message) => {
		// Обновляем время последнего запроса
		clientActions[userId].lastRequestTime = new Date(); 
		
		// Проверяем, является ли сообщение экземпляром Buffer
		if (Buffer.isBuffer(message))
			message = message.toString(); 
		try {
			// Преобразуем строку JSON обратно в объект
			message = JSON.parse(message); 
			//Получаем историю чата из запроса
			let messages = message.messages;
			let model = message.model;
			let rewrite = message.rewrite;
			
			// Выполняем валидацию сообщений
			messages = validateMessages(messages);
			
			// Проверяем, остались ли сообщения после валидации
			if (messages.length === 0) {
				const response = { code: 2, message: "Нет валидных сообщений для обработки." };
				sendMessage(ws, response);
				return;
			}
			
			// Урезаем последнее сообщение до 2000 символов, если оно от клиента
			if (messages[messages.length - 1].role === 'user') {
				messages[messages.length - 1].content = messages[messages.length - 1].content.substring(0, 2000000);
			}
			
			// Увеличиваем общий счетчик запросов 
            stats.totalRequests++;
			
			// Задержка перед отправкой запроса
			const responseTimes = clientActions[userId].responseTimes; // Получаем массив responseTimes

			// Проверяем, прошло ли больше 15 секунд с момента окончания последнего ответа
			if (responseTimes.length === 3) {
				const lastEndTime = responseTimes[responseTimes.length - 1].end;
				const currentTime = new Date();

				// Вычисляем разницу во времени
				const timeSinceLastEnd = currentTime - lastEndTime; // Время в миллисекундах

				if (timeSinceLastEnd > 20000) { // Если прошло больше 15 секунд
					clientActions[userId].delay = 1000; // Устанавливаем delay на 1 мс
				}
			}
			console.log('Задержка - ', clientActions[userId].delay)
			// Используем Bottleneck для управления количеством запросов
			limiters[userId].schedule(() => {
				// Отправка запроса на сервер чата с задержкой
				setTimeout(() => {
					call(messages, model, ws, userId, rewrite);
				}, clientActions[userId].delay);
			}).catch(error => {
				stats.totalIPBlocks++;
				//console.error('Rate limit exceeded, message ignored:', error);
				//const response = { code: 2, message: "Превышен лимит запросов. Пожалуйста, попробуйте позже." };
				// Отправка ответа клиенту
				//sendMessage(ws, response);
			});
			
		} catch (error) {
				stats.totalWsMessErrors++;
				console.error('Error parsing JSON:', error);
				const response = { code: 2, message: "Произошла ошибка. Попробуйте еще раз."};
				console.log(response);
				// Отправка ответа клиенту
				sendMessage(ws, response);
		}
		
    });

    ws.on('close', () => {
		try {
			// Проверка, существует ли userId в clientActions
			if (clientActions[userId]) {
				clientActions[userId].connected = false; // Обновление статуса подключения
				
			}
			
			console.log('Client ',userId, ' disconnected'); 
			// Уменьшаем счетчик подключений для этого IP		
			if (ipConnectionCount[userIP] !== undefined) {
				ipConnectionCount[userIP]--;
				
				// Если счетчик равен 0, удаляем запись из объекта
				if (ipConnectionCount[userIP] === 0) {
					delete ipConnectionCount[userIP];
				}
			}
			// Уменьшаем счетчик при отключении клиента
			currentConnectedClients = Math.max(currentConnectedClients - 1, 0); 
		} catch (error) {
			console.error('Error handling client disconnect:', error);
		}
    });
});

Bottleneck контролирует очередь запросов от пользователя, если он отправил запрос и сервер еще генирирует ответ, то второй запрос согласно настройкам игнорируется. Однако это перестает работать, если пользователь перезагрузит страницу, после этого он может отправлять сколько угодно запросов не дожидаясь ответов. userId при этом всегда один и тот же, фронтенд берет его из localstorage. Почему?