#ifdef NETWORK

#include "WebSocketClient.h"
#include <iostream>
#include <SDL2/SDL.h>

// Вспомогательный split
std::vector<std::string> split(const std::string& s, char delimiter) {
	std::vector<std::string> tokens;
	std::string token;
	std::istringstream tokenStream(s);
	while (std::getline(tokenStream, token, delimiter)) {
		tokens.push_back(token);
	}
	return tokens;
}

namespace ZL {

	void WebSocketClient::Connect(const std::string& host, uint16_t port) {
		try {
			boost::asio::ip::tcp::resolver resolver(ioc_);
			auto const results = resolver.resolve(host, std::to_string(port));

			ws_ = std::make_unique<boost::beast::websocket::stream<boost::beast::tcp_stream>>(ioc_);

			// Выполняем синхронный коннект и handshake для простоты старта
			boost::beast::get_lowest_layer(*ws_).connect(results);
			ws_->handshake(host, "/");

			connected = true;

			// Запускаем асинхронное чтение в пуле потоков TaskManager
			startAsyncRead();

		}
		catch (std::exception& e) {
			std::cerr << "Network Error: " << e.what() << std::endl;
		}
	}

	void WebSocketClient::startAsyncRead() {
		ws_->async_read(buffer_, [this](boost::beast::error_code ec, std::size_t bytes) {
			if (!ec) {
				std::string msg = boost::beast::buffers_to_string(buffer_.data());
				buffer_.consume(bytes);
				processIncomingMessage(msg);
				startAsyncRead();
			}
			else {
				connected = false;
			}
			});
	}

	void WebSocketClient::processIncomingMessage(const std::string& msg) {
		// Логика парсинга...
		if (msg.rfind("ID:", 0) == 0) {
			clientId = std::stoi(msg.substr(3));
		}

		// Безопасно кладем в очередь для главного потока
		std::lock_guard<std::mutex> lock(queueMutex);
		messageQueue.push(msg);
	}

	std::vector<ProjectileInfo> WebSocketClient::getPendingProjectiles() {
		std::lock_guard<std::mutex> lock(projMutex_);
		auto copy = pendingProjectiles_;
		pendingProjectiles_.clear();
		return copy;
	}

	std::vector<DeathInfo> WebSocketClient::getPendingDeaths() {
		std::lock_guard<std::mutex> lock(deathsMutex_);
		auto copy = pendingDeaths_;
		pendingDeaths_.clear();
		return copy;
	}

	std::vector<int> WebSocketClient::getPendingRespawns() {
		std::lock_guard<std::mutex> lock(respawnMutex_);
		auto copy = pendingRespawns_;
		pendingRespawns_.clear();
		return copy;
	}

	std::vector<BoxDestroyedInfo> WebSocketClient::getPendingBoxDestructions() {
		std::lock_guard<std::mutex> lock(boxDestructionsMutex_);
		auto copy = pendingBoxDestructions_;
		pendingBoxDestructions_.clear();
		return copy;
	}

	void WebSocketClient::Poll() {
		std::lock_guard<std::mutex> lock(queueMutex);

		while (!messageQueue.empty()) {

			auto nowTime = std::chrono::system_clock::now();

			//Apply server delay:
			nowTime -= std::chrono::milliseconds(CLIENT_DELAY);

			auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
				nowTime.time_since_epoch()
			).count();



			std::string msg = messageQueue.front();
			messageQueue.pop();

			// Обработка списка коробок от сервера
			if (msg.rfind("BOXES:", 0) == 0) {
				std::string payload = msg.substr(6); // после "BOXES:"
				std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> parsedBoxes;
				if (!payload.empty()) {
					auto items = split(payload, '|');
					for (auto& item : items) {
						if (item.empty()) continue;
						auto parts = split(item, ':');
						if (parts.size() < 7) continue;
						try {
							float px = std::stof(parts[0]);
							float py = std::stof(parts[1]);
							float pz = std::stof(parts[2]);
							Eigen::Quaternionf q(
								std::stof(parts[3]),
								std::stof(parts[4]),
								std::stof(parts[5]),
								std::stof(parts[6])
							);
							Eigen::Matrix3f rot = q.toRotationMatrix();
							parsedBoxes.emplace_back(Eigen::Vector3f{ px, py, pz }, rot);
						}
						catch (...) {
							// пропускаем некорректную запись
							continue;
						}
					}
				}
				{
					std::lock_guard<std::mutex> bLock(boxesMutex);
					serverBoxes_ = std::move(parsedBoxes);
				}
				continue;
			}
			if (msg.rfind("RESPAWN_ACK:", 0) == 0) {
				auto parts = split(msg, ':');
				if (parts.size() >= 2) {
					try {
						int respawnedPlayerId = std::stoi(parts[1]);
						{
							std::lock_guard<std::mutex> rLock(respawnMutex_);
							pendingRespawns_.push_back(respawnedPlayerId);
						}
						{
							std::lock_guard<std::mutex> pLock(playersMutex);
							remotePlayers.erase(respawnedPlayerId);
						}
						std::cout << "Client: Received RESPAWN_ACK for player " << respawnedPlayerId << std::endl;
					}
					catch (...) {}
				}
				continue;
			}

			if (msg.rfind("BOX_DESTROYED:", 0) == 0) {
				auto parts = split(msg, ':');
				if (parts.size() >= 7) {
					try {
						BoxDestroyedInfo destruction;
						destruction.boxIndex = std::stoi(parts[1]);
						destruction.serverTime = std::stoull(parts[2]);
						destruction.position = Eigen::Vector3f(
							std::stof(parts[3]),
							std::stof(parts[4]),
							std::stof(parts[5])
						);
						destruction.destroyedBy = std::stoi(parts[6]);

						{
							std::lock_guard<std::mutex> lock(boxDestructionsMutex_);
							pendingBoxDestructions_.push_back(destruction);
						}

						std::cout << "Client: Received BOX_DESTROYED for box " << destruction.boxIndex
							<< " destroyed by player " << destruction.destroyedBy << std::endl;
					}
					catch (const std::exception& e) {
						std::cerr << "Client: Error parsing BOX_DESTROYED: " << e.what() << std::endl;
					}
				}
				continue;
			}

			if (msg.rfind("PROJECTILE:", 0) == 0) {
				auto parts = split(msg, ':');
				if (parts.size() >= 10) {
					try {
						ProjectileInfo pi;
						pi.shooterId = std::stoi(parts[1]);
						pi.clientTime = std::stoull(parts[2]);
						pi.position = Eigen::Vector3f(
							std::stof(parts[3]),
							std::stof(parts[4]),
							std::stof(parts[5])
						);
						Eigen::Quaternionf q(
							std::stof(parts[6]),
							std::stof(parts[7]),
							std::stof(parts[8]),
							std::stof(parts[9])
						);
						pi.rotation = q.toRotationMatrix();

						pi.velocity = std::stof(parts[10]);
						std::lock_guard<std::mutex> pl(projMutex_);
						pendingProjectiles_.push_back(pi);
					}
					catch (...) {}
				}
				continue;
			}

			if (msg.rfind("DEAD:", 0) == 0) {
				auto parts = split(msg, ':');
				if (parts.size() >= 7) {
					try {
						DeathInfo di;
						di.serverTime = std::stoull(parts[1]);
						di.targetId = std::stoi(parts[2]);
						di.position = Eigen::Vector3f(
							std::stof(parts[3]),
							std::stof(parts[4]),
							std::stof(parts[5])
						);
						di.killerId = std::stoi(parts[6]);

						std::lock_guard<std::mutex> dl(deathsMutex_);
						pendingDeaths_.push_back(di);
					}
					catch (...) {}
				}
				continue;
			}

			if (msg.rfind("EVENT:", 0) == 0) {
				auto parts = split(msg, ':');
				if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...

				int remoteId = std::stoi(parts[1]);
				std::string subType = parts[2];
				uint64_t sentTime = std::stoull(parts[3]);

				ClientState remoteState;
				remoteState.id = remoteId;

				std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast<std::chrono::system_clock::time_point::duration>(std::chrono::milliseconds(sentTime)) };
				remoteState.lastUpdateServerTime = uptime_timepoint;

				if (subType == "UPD") {
					int startFrom = 4;
					remoteState.position = { std::stof(parts[startFrom]), std::stof(parts[startFrom + 1]), std::stof(parts[startFrom + 2]) };
					Eigen::Quaternionf q(
						std::stof(parts[startFrom + 3]),
						std::stof(parts[startFrom + 4]),
						std::stof(parts[startFrom + 5]),
						std::stof(parts[startFrom + 6]));
					remoteState.rotation = q.toRotationMatrix();

					remoteState.currentAngularVelocity = Eigen::Vector3f{
						std::stof(parts[startFrom + 7]),
						std::stof(parts[startFrom + 8]),
						std::stof(parts[startFrom + 9]) };
					remoteState.velocity = std::stof(parts[startFrom + 10]);
					remoteState.selectedVelocity = std::stoi(parts[startFrom + 11]);
					remoteState.discreteMag = std::stof(parts[startFrom + 12]);
					remoteState.discreteAngle = std::stoi(parts[startFrom + 13]);
				}
				else
				{
					throw std::runtime_error("Unknown EVENT subtype: " + subType);
				}

				{
					std::lock_guard<std::mutex> pLock(playersMutex);
					auto& rp = remotePlayers[remoteId];

					rp.add_state(remoteState);
				}
			}

			if (msg.rfind("SNAPSHOT:", 0) == 0) {
				auto mainParts = split(msg.substr(9), '|'); // Отсекаем "SNAPSHOT:" и делим по игрокам
				if (mainParts.empty()) continue;

				uint64_t serverTimestamp = std::stoull(mainParts[0]);
				std::chrono::system_clock::time_point serverTime{ std::chrono::milliseconds(serverTimestamp) };

				for (size_t i = 1; i < mainParts.size(); ++i) {
					auto playerParts = split(mainParts[i], ':');
					if (playerParts.size() < 15) continue; // ID + 14 полей ClientState

					int rId = std::stoi(playerParts[0]);
					if (rId == clientId) continue; // Свое состояние игрок знает лучше всех (Client Side Prediction)

					ClientState remoteState;
					remoteState.id = rId;
					remoteState.lastUpdateServerTime = serverTime;

					// Используем твой handle_full_sync, начиная со 2-го индекса (пропускаем ID в playerParts)
					remoteState.handle_full_sync(playerParts, 1);

					{
						std::lock_guard<std::mutex> pLock(playersMutex);
						remotePlayers[rId].add_state(remoteState);
					}
				}
				continue;
			}

		}
	}
	void WebSocketClient::Send(const std::string& message) {
		if (!connected) return;

		auto ss = std::make_shared<std::string>(message);

		std::lock_guard<std::mutex> lock(writeMutex_);
		writeQueue_.push(ss);

		// Если сейчас ничего не записывается, инициируем первую запись
		if (!isWriting_) {
			doWrite();
		}
	}

	void WebSocketClient::doWrite() {
		// Эта функция всегда вызывается под мьютексом или из колбэка
		if (writeQueue_.empty()) {
			isWriting_ = false;
			return;
		}

		isWriting_ = true;
		auto message = writeQueue_.front();

		// Захватываем self (shared_from_this), чтобы объект не удалился во время записи
		ws_->async_write(
			boost::asio::buffer(*message),
			[this, message](boost::beast::error_code ec, std::size_t) {
				if (ec) {
					connected = false;
					return;
				}

				std::lock_guard<std::mutex> lock(writeMutex_);
				writeQueue_.pop(); // Удаляем отправленное сообщение
				doWrite();         // Проверяем следующее
			}
		);
	}
}

#endif
