#ifdef NETWORK

#include "WebSocketClientBase.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 WebSocketClientBase::HandlePollMessage(const std::string& msg) {
		auto parts = split(msg, ':');
		if (parts.empty()) return;

		if (parts[0] == "ID") {
			std::cout << "ID Message Received:" << msg << std::endl;
			clientId = std::stoi(parts[1]);
			if (parts.size() >= 3) {
				std::cout << "ID Message Received step 2" << std::endl;
				uint64_t serverTime = std::stoull(parts[2]);
				uint64_t localTime = std::chrono::duration_cast<std::chrono::milliseconds>(
					std::chrono::system_clock::now().time_since_epoch()).count();

				std::cout << "ID Message Received localTime = " << localTime << std::endl;
				std::cout << "ID Message Received serverTime = " << serverTime << std::endl;

				// Вычисляем смещение
				timeOffset = static_cast<int64_t>(serverTime) - static_cast<int64_t>(localTime);

				std::cout << "Time synchronized. Offset: " << timeOffset << " ms" << std::endl;
			}
			return;
		}

		if (msg.rfind("BOXES:", 0) == 0) {
			std::string payload = msg.substr(6);
			std::vector<std::tuple<int, Eigen::Vector3f, Eigen::Matrix3f, bool>> parsed;
			if (!payload.empty()) {
				auto items = split(payload, '|');
				for (auto& item : items) {
					if (item.empty()) continue;
					auto parts = split(item, ':');
					if (parts.size() < 9) {
						return;
					}
					try {
						int idx = std::stoi(parts[0]);
						float px = std::stof(parts[1]);
						float py = std::stof(parts[2]);
						float pz = std::stof(parts[3]);
						Eigen::Quaternionf q(
							std::stof(parts[4]),
							std::stof(parts[5]),
							std::stof(parts[6]),
							std::stof(parts[7])
						);
						bool destroyed = (std::stoi(parts[8]) != 0);

						Eigen::Matrix3f rot = q.toRotationMatrix();
						parsed.emplace_back(idx, Eigen::Vector3f{ px, py, pz }, rot, destroyed);
					}
					catch (...) {
						return;
					}
				}
			}

			int maxIdx = -1;
			for (auto& t : parsed) {
				int idx = std::get<0>(t);
				if (idx > maxIdx) maxIdx = idx;
			}
			if (maxIdx < 0) {
				serverBoxes_.clear();
				serverBoxesDestroyed_.clear();
				return;
			}

			serverBoxes_.clear();
			serverBoxes_.resize((size_t)maxIdx + 1);
			serverBoxesDestroyed_.clear();
			serverBoxesDestroyed_.resize((size_t)maxIdx + 1, true);

			for (auto& t : parsed) {
				int idx = std::get<0>(t);
				const Eigen::Vector3f& pos = std::get<1>(t);
				const Eigen::Matrix3f& rot = std::get<2>(t);
				bool destroyed = std::get<3>(t);
				if (idx >= 0 && idx < serverBoxes_.size()) {
					serverBoxes_[idx] = { pos, rot };
					serverBoxesDestroyed_[idx] = destroyed;
				}
			}

			return;
		}
		if (msg.rfind("PLAYER_LEFT:", 0) == 0) {
			if (parts.size() >= 2) {
				try {
					int pid = std::stoi(parts[1]);
					remotePlayers.erase(pid);
					pendingDisconnects_.push_back(pid);
					std::cout << "Client: Player " << pid << " disconnected (PLAYER_LEFT)\n";
				}
				catch (...) {}
			}
			return;
		}

		if (msg.rfind("RESPAWN_ACK:", 0) == 0) {
			//auto parts = split(msg, ':');
			if (parts.size() >= 2) {
				try {
					int respawnedPlayerId = std::stoi(parts[1]);
					pendingRespawns_.push_back(respawnedPlayerId);
					std::cout << "Client: Received RESPAWN_ACK for player " << respawnedPlayerId << std::endl;
				}
				catch (...) {}
			}
			return;
		}

		if (msg.rfind("BOX_PICKED_UP:", 0) == 0) {
			if (parts.size() >= 3) {
				try {
					BoxPickedUpInfo pickup;
					pickup.boxIndex  = std::stoi(parts[1]);
					pickup.pickedUpBy = std::stoi(parts[2]);
					pendingBoxPickups_.push_back(pickup);
					std::cout << "Client: Received BOX_PICKED_UP box=" << pickup.boxIndex
						<< " by player " << pickup.pickedUpBy << std::endl;
				}
				catch (...) {}
			}
			return;
		}

		if (msg.rfind("BOX_RESPAWN:", 0) == 0) {
			if (parts.size() >= 9) {
				try {
					BoxRespawnInfo respawn;
					respawn.boxIndex = std::stoi(parts[1]);
					float px = std::stof(parts[2]);
					float py = std::stof(parts[3]);
					float pz = std::stof(parts[4]);
					Eigen::Quaternionf q(
						std::stof(parts[5]),
						std::stof(parts[6]),
						std::stof(parts[7]),
						std::stof(parts[8])
					);
					respawn.position = Eigen::Vector3f(px, py, pz);
					respawn.rotation = q.toRotationMatrix();
					pendingBoxRespawns_.push_back(respawn);
					std::cout << "Client: Received BOX_RESPAWN box=" << respawn.boxIndex << std::endl;
				}
				catch (...) {}
			}
			return;
		}

		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]);

					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;
				}
			}
			return;
		}

		if (msg.rfind("PROJECTILE:", 0) == 0) {
			//auto parts = split(msg, ':');
			if (parts.size() >= 11) {
				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]);
					pendingProjectiles_.push_back(pi);
				}
				catch (...) {}
			}
			return;
		}

		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]);

					pendingDeaths_.push_back(di);
				}
				catch (...) {}
			}
			return;
		}
		if (msg.rfind("SPAWN:", 0) == 0) {
			// SPAWN:playerId:serverTime:<14 полей>
			if (parts.size() >= 3 + 14) {
				try {
					int pid = std::stoi(parts[1]);
					uint64_t serverTime = std::stoull(parts[2]);

					ClientState st;
					st.id = pid;
					std::chrono::system_clock::time_point tp{ std::chrono::milliseconds(serverTime) };
					st.lastUpdateServerTime = tp;

					// данные начинаются с parts[3]
					st.handle_full_sync(parts, 3);

					pendingSpawns_.push_back(st);
					std::cout << "Client: SPAWN received for player " << pid << std::endl;
				}
				catch (...) {}
			}
			return;
		}
		if (msg.rfind("EVENT:", 0) == 0) {
			//auto parts = split(msg, ':');
			if (parts.size() < 5) return; // 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);
			}

			{
				auto& rp = remotePlayers[remoteId];
				if (!rp.timedStates.empty()) {
					const ClientState& last = rp.timedStates.back();
					remoteState.nickname = last.nickname;
					remoteState.shipType = last.shipType;
				}
				rp.add_state(remoteState);
			}
		}

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

			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) return; // ID + 14 полей ClientState

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

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

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

				remotePlayers[rId].add_state(remoteState);
			}
		}

		if (msg.rfind("PLAYERINFO:", 0) == 0) {
			if (parts.size() >= 4) {
				try {
					int pid = std::stoi(parts[1]);
					if (pid == clientId) {
						return;
					}

					std::string nick = parts[2];
					int st = std::stoi(parts[3]);

					auto it = remotePlayers.find(pid);
					if (it != remotePlayers.end() && !it->second.timedStates.empty()) {
						auto& states = it->second.timedStates;
						states.back().nickname = nick;
						states.back().shipType = st;
					}
					else {
						ClientState cs;
						cs.id = pid;
						cs.nickname = nick;
						cs.shipType = st;
						cs.lastUpdateServerTime = std::chrono::system_clock::now();
						remotePlayers[pid].add_state(cs);
					}

					std::cout << "Client: PLAYERINFO received. id=" << pid << " nick=" << nick << " shipType=" << st << std::endl;
				}
				catch (...) {
				}
			}
			return;
		}
	}

	std::string WebSocketClientBase::SignMessage(const std::string& msg) {
#ifdef ENABLE_NETWORK_CHECKSUM
		size_t hashValue = fnv1a_hash(msg + NET_SECRET);
		std::stringstream ss;
		ss << msg << "#hash:" << std::hex << hashValue;
		return ss.str();
#else
		return msg;
#endif
	}

	std::vector<ProjectileInfo> WebSocketClientBase::getPendingProjectiles() {
		std::vector<ProjectileInfo> copy;
		copy.swap(pendingProjectiles_);
		return copy;
	}

	std::vector<DeathInfo> WebSocketClientBase::getPendingDeaths() {
		std::vector<DeathInfo> copy;
		copy.swap(pendingDeaths_);
		return copy;
	}

	std::vector<int> WebSocketClientBase::getPendingRespawns() {
		std::vector<int> copy;
		copy.swap(pendingRespawns_);
		return copy;
	}

	std::vector<BoxDestroyedInfo> WebSocketClientBase::getPendingBoxDestructions() {
		std::vector<BoxDestroyedInfo> copy;
		copy.swap(pendingBoxDestructions_);
		return copy;
	}

	std::vector<BoxPickedUpInfo> WebSocketClientBase::getPendingBoxPickups() {
		std::vector<BoxPickedUpInfo> copy;
		copy.swap(pendingBoxPickups_);
		return copy;
	}

	std::vector<BoxRespawnInfo> WebSocketClientBase::getPendingBoxRespawns() {
		std::vector<BoxRespawnInfo> copy;
		copy.swap(pendingBoxRespawns_);
		return copy;
	}

	std::vector<int> WebSocketClientBase::getPendingDisconnects() {
		std::vector<int> copy;
		copy.swap(pendingDisconnects_);
		return copy;
	}

	std::vector<ClientState> WebSocketClientBase::getPendingSpawns() {
		std::vector<ClientState> copy;
		copy.swap(pendingSpawns_);
		return copy;
	}
}

#endif