#include <string>
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
#include <thread>
#include <vector>
#include <random>
#include <sstream>
#include <fstream>
#include <openssl\\rand.h>
#include <sqlite3.h>
#include <filesystem>
#include <mutex>
#include "Modules\\Logger.h"
#include "Modules\\inicpp.hpp"
#include "Modules\\dbActions.h"
// #include "Modules/serverFrontEndBridge.h"
//#include "Modules/datapackProcess.h"
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "sqlite3.lib") 

using inicpp::ini;
using inicpp::IniManager;
using std::string;
using std::to_string;
using std::thread;
using std::vector;
using std::mt19937;
using std::random_device;
using std::uniform_int_distribution;
using std::filesystem::create_hard_link;
using std::filesystem::read_symlink;
using std::stoi;

int isDebug = 1;
string db_path = ".\\database.db";
sqlite3* db;
int tokenLength = 30; // Length of user token

struct structUserInfo {
	int ID;
	string name[65525];
	string passwd[65525];
	int port_IDMap[65525];
	int ID_PortMap[65525];
	string ID_TokenMap[65525];
} userInfo;

struct structIpAddr {
	const char* server_cstr = "127.0.0.1";
	struct in_addr server;
	vector<char> server_vec;
	int listenPort = 23000;
} ipAddr;

struct publicSock {
	SOCKET client;
	SOCKET service;
	SOCKET sysAdmin;
} globalSock;

int serverInit_Sockets(); // Initialize socket
void userExit(int userID, int clientPort); // Process user exit event
string getTextMiddle(string source, string left, string right); // Get a text from a text
bool tcpSend(SOCKET sock, string str);
bool verifyTimestamp(const string& timestampStr);
string getRandomStr(const int len);
int serverInit_SQLite_Open();
int serverInit_Sockets();

// UTF-8 encoding helper functions
bool isValidUtf8(const char* str, size_t length);
string ensureUtf8(const string& str);

int main() {
	postLog("[System] Starting Teacher Salary Management Server...", 1);
	if (serverInit_SQLite_Open() != 0) {
		postLog("[System] Failed to initialize SQLite database.", 4);
		return 1;
	}
	serverInit_Sockets();

	closesocket(globalSock.client);
	WSACleanup();
}

// Check if the given string is valid UTF-8
bool isValidUtf8(const char* str, size_t length) {
	size_t i = 0;
	while (i < length) {
		unsigned char c = static_cast<unsigned char>(str[i]);
		if (c <= 0x7F) {
			i++;
		}
		else if (c >= 0xC2 && c <= 0xDF) {
			if (i + 1 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80) return false;
			i += 2;
		}
		else if (c == 0xE0) {
			if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				static_cast<unsigned char>(str[i + 1]) < 0xA0 ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
			i += 3;
		}
		else if (c >= 0xE1 && c <= 0xEC) {
			if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
			i += 3;
		}
		else if (c == 0xED) {
			if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				static_cast<unsigned char>(str[i + 1]) > 0x9F ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
			i += 3;
		}
		else if (c >= 0xEE && c <= 0xEF) {
			if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
			i += 3;
		}
		else if (c == 0xF0) {
			if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				static_cast<unsigned char>(str[i + 1]) < 0x90 ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 3]) & 0xC0) != 0x80) return false;
			i += 4;
		}
		else if (c >= 0xF1 && c <= 0xF3) {
			if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 3]) & 0xC0) != 0x80) return false;
			i += 4;
		}
		else if (c == 0xF4) {
			if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
				static_cast<unsigned char>(str[i + 1]) > 0x8F ||
				(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
				(static_cast<unsigned char>(str[i + 3]) & 0xC0) != 0x80) return false;
			i += 4;
		}
		else {
			return false;
		}
	}
	return true;
}

// Ensure string is valid UTF-8, replace invalid sequences if needed
string ensureUtf8(const string& str) {
	if (str.empty()) return str;

	// Check if the string is already valid UTF-8
	if (isValidUtf8(str.c_str(), str.length())) {
		return str;
	}

	// If invalid UTF-8, try to convert from common encodings
	// This is a simple fallback for invalid UTF-8
	string result;
	for (size_t i = 0; i < str.length(); i++) {
		unsigned char c = static_cast<unsigned char>(str[i]);
		if (c <= 0x7F) {
			result += str[i];
		}
		else {
			// Replace non-ASCII characters with '?' for simplicity
			// In a production system, you'd want proper encoding detection and conversion
			result += '?';
		}
	}

	postLog("[Encoding] Invalid UTF-8 detected, replaced non-ASCII characters", 2);
	return result;
}

void handle_client(SOCKET sock, string clientIP, int clientPort) {
	constexpr int MAX_USERS = 65525;
	auto inRange = [&](int idx) { return idx >= 0 && idx < MAX_USERS; };

	char buffer[1024];
	int bytesReceived;
	postLog("[Socket] New connection from " + clientIP + ":" + to_string(clientPort), 0);

	bool keepRunning = true;
	while (keepRunning) {
		bytesReceived = recv(sock, buffer, sizeof(buffer), 0);

		if (bytesReceived > 0) {
			// Normal data received
			string received(buffer, bytesReceived);
			if (received.empty()) {
				postLog("[Socket] Received empty payload, closing socket.", 3);
				break;
			}

			// Ensure received data is valid UTF-8
			string processedReceived = ensureUtf8(received);
			if (processedReceived != received) {
				postLog("[Socket] Received data contains invalid UTF-8, processed", 2);
			}

			size_t leftControlSign = processedReceived.find("[");
			size_t rightControlSign = processedReceived.find("]");
			if (leftControlSign != string::npos && rightControlSign != string::npos && rightControlSign > leftControlSign) {
				string cmdType = processedReceived.substr(leftControlSign + 1, rightControlSign - leftControlSign - 1);
				postLog("[Socket] Command received: " + cmdType, 0);
				size_t argStart = processedReceived.find_first_not_of(" ", rightControlSign + 1);
				string argument = (argStart == string::npos) ? string() : processedReceived.substr(argStart);
				postLog("[Socket] Argument received: " + argument, 0);

				// debug — keep connection open
				if (cmdType == "debug") {
					continue;
				}

				// new user registration — keep connection open on errors, close after successful register
				if (cmdType == "newUserRegist") {
					string newUserName = getTextMiddle(argument, "<userName>", "</userName>");
					string newUserPasswd = getTextMiddle(argument, "<userPasswd>", "</userPasswd>");
					string newUsertimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
					int newUserID = -1;
					if (newUserName.empty() || newUserPasswd.empty() || newUsertimeStamp.empty()) {
						tcpSend(sock, "[Regist_Rejected] Invalid registration information.");
						postLog("[User.Auth] Received invalid regist request.", 2);
						continue;
					}
					if (!verifyTimestamp(newUsertimeStamp)) {
						tcpSend(sock, "[Regist_Rejected] Invalid timestamp.");
						postLog("[User.Auth] Received invalid timestamp: " + newUsertimeStamp, 2);
						continue;
					}
					if (newUserName.length() > 20 || newUserPasswd.length() > 20) {
						tcpSend(sock, "[Regist_Rejected] Username or password too long.");
						postLog("[User.Auth] Received too long username or password: " + newUserName + " / " + newUserPasswd, 2);
						continue;
					}
					for (int i = 1; i < MAX_USERS; i++) {
						if (db_getItem(db, "users", "userID", i) != "") {
							continue;
						}
						else {
							newUserID = i;
							break;
						}
					}
					if (newUserID == -1) {
						tcpSend(sock, "[Regist_Rejected] User limit reached.");
						postLog("[User.Auth] No available userID found, user limit reached.", 2);
						continue;
					}
					if (db_getItem(db, "users", "userName", newUserID) != "") {
						tcpSend(sock, "[Regist_Rejected] Username already existed.");
						postLog("[User.Auth] Username already existed.", 2);
						continue;
					}
					postLog("[Socket] Receive legal regist request - ID: " + to_string(newUserID) + ", Name: " + newUserName + ", userPasswd: " + newUserPasswd, 1);
					//db_addItem(db, "users", newUserID, "userName", newUserName);
					//db_addItem(db, "users", newUserID, "Passwd", newUserPasswd);
					tcpSend(sock, "[Regist_Accepted] User registered successfully.");
					postLog("[Socket] New user registed: " + newUserName, 1);
					// close connection after successful registration
					break;
				}

				/*
				User Login Args:
				- userName
				- userPasswd
				- timeStamp
				*/
				if (cmdType == "newUserEntry") {
					int newUserID;
					string newUserName = getTextMiddle(argument, "<userName>", "</userName>");
					string newUserPasswd = getTextMiddle(argument, "<userPasswd>", "</userPasswd>");
					string newUsertimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
					if (newUserName == "" || newUserPasswd == "" || newUsertimeStamp == "") {
						tcpSend(sock, "[Login_Rejected] Invalid registration information.");
						postLog("[Socket] Received invalid regist request.", 2);
						continue;
					}
					if (!verifyTimestamp(newUsertimeStamp)) {
						tcpSend(sock, "[Login_Rejected] Invalid timestamp.");
						postLog("[Socket] Received invalid timestamp: " + newUsertimeStamp, 2);
						continue;
					}
					if (newUserName.length() > 20 || newUserPasswd.length() > 20) {
						tcpSend(sock, "[Login_Rejected] Username or password too long.");
						postLog("[Socket] Received too long username or password: " + newUserName + " / " + newUserPasswd, 2);
						continue;
					}
					newUserID = stoi(db_findIdBy(db, "users", "userName", newUserName));
					if (newUserID != 0) {
						if (!(newUserName == db_getItem(db, "users", "userName", newUserID) && newUserPasswd == db_getItem(db, "users", "Passwd", newUserID))) {
							tcpSend(sock, "[Login_Rejected] Invalid username or password.");
							postLog("[Socket] Invalid username or password for user: " + newUserName, 2);
							continue;
						}
					}
					else {
						tcpSend(sock, "[Login_Rejected] User does not exist.");
						postLog("[Socket] User does not exist: " + newUserName, 2);
						continue;
					}

					postLog("[Socket] Receive loggin request - ID: " + to_string(newUserID) + ", Name: " + newUserName + ", userPasswd: " + newUserPasswd, 1);
					// New user logged in successfully — keep connection open
					postLog("[Socket] New user entry: " + newUserName, 1);
					postLog("[Socket] userInfo.ID_PortMap[" + to_string(clientPort) + "] = " + to_string(userInfo.ID_PortMap[clientPort]), 0);
					if (userInfo.ID_PortMap[newUserID] == 0) { // Record user info to specific port
						userInfo.name[newUserID] = newUserName;
						userInfo.passwd[newUserID] = newUserPasswd;
						userInfo.port_IDMap[clientPort] = newUserID;
						userInfo.ID_PortMap[newUserID] = clientPort;
						userInfo.ID_TokenMap[newUserID] = getRandomStr(tokenLength); // Generate token
						tcpSend(sock, "[Login_Accepted] <userName>" + userInfo.name[newUserID] + "</userName> <userID>" + to_string(newUserID) + "</userID> <token>" + userInfo.ID_TokenMap[newUserID] + "</token>");
						postLog("[Socket] User [" + to_string(newUserID) + "]" + newUserName + ":" + to_string(clientPort) + " logged in successfully.", 1);
						// keep the connection active to continue monitoring disconnects
						continue;
					}
					else {
						postLog("[Socket] User [" + to_string(newUserID) + "]" + newUserName + ":" + to_string(clientPort) + " already existed: [" + to_string(newUserID) + "]" + userInfo.name[newUserID] + ":" + to_string(userInfo.ID_PortMap[newUserID]), 2);
						tcpSend(sock, "[Login_Rejected] User already logged in.");
						continue;
					}
				}

				if (cmdType == "userExit") { // User logout
					int uid = userInfo.port_IDMap[clientPort];
					if (getTextMiddle(argument, "<token>", "</token>") != userInfo.ID_TokenMap[uid]) { // Token verification failed
						tcpSend(sock, "[Exit_Rejected] Invalid token.");
						postLog("[Socket] User [" + to_string(uid) + "]" + userInfo.name[uid] + " exit rejected due to invalid token.", 2);
						continue;
					}
					if (getTextMiddle(argument, "<userID>", "</userID>") != to_string(uid)) { // UserID verification failed
						tcpSend(sock, "[Exit_Rejected] Invalid userID.");
						postLog("[Socket] User [" + to_string(uid) + "]" + userInfo.name[uid] + " exit rejected due to invalid userID.", 2);
						continue;
					}
					postLog("[Socket] User [" + userInfo.name[uid] + "] Exited normally.", 1);
					userExit(userInfo.port_IDMap[clientPort], clientPort);
					tcpSend(sock, "[Exit_Accepted] User exited successfully.");
					// close connection after normal exit
					break;
				}

				if (cmdType == "userGet") {
					int getUserID = stoi(getTextMiddle(argument, "<userID>", "</userID>")); // Get request parameters
					string getToken = getTextMiddle(argument, "<token>", "</token>");
					string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
					string getTable = getTextMiddle(argument, "<table>", "</table>");
					string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
					string getValueType = getTextMiddle(argument, "<valueType>", "</valueType>");

					if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "" || getValueType == "") {
						tcpSend(sock, "[Get_Rejected] Invalid get information.");
						postLog("[Socket] Received invalid get request.", 2);
						continue;
					}
					if (!verifyTimestamp(getTimeStamp)) {
						tcpSend(sock, "[Get_Rejected] Invalid timestamp.");
						postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2);
						continue;
					}

					postLog("[Socket] Receive get request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID + ", valueType: " + getValueType, 1);
					if (getToken != userInfo.ID_TokenMap[getUserID]) {
						tcpSend(sock, "[Get_Rejected] Invalid token.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to invalid token.", 2);
						continue;
					}
					if ((char)getItemID.c_str() <= '9' && (char)getItemID.c_str() >= '0') {
						getItemID = db_findIdBy(db, getTable, "teacherName", getItemID);
						if (getItemID == "0") {
							tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
							postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
							continue;
						}
					}
					if (db_getItem(db, getTable, "teacherName", stoi(getItemID)) == "err: no specific id") {
						tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
						continue;
					}
					if (getTable == "salaryForm") {
						if (getValueType == "all") {
							if (db_getItem(db, "salaryForm", "teacherName", stoi(getItemID)) != "") {
								string basicSalary = db_getItem(db, "salaryForm", "basicSalary", stoi(getItemID));
								string allowances = db_getItem(db, "salaryForm", "allowances", stoi(getItemID));
								string livingAllowances = db_getItem(db, "salaryForm", "livingAllowances", stoi(getItemID));
								string phoneCost = db_getItem(db, "salaryForm", "phoneCost", stoi(getItemID));
								string waterPowerCost = db_getItem(db, "salaryForm", "waterPowerCost", stoi(getItemID));
								string houseRent = db_getItem(db, "salaryForm", "houseRent", stoi(getItemID));
								string incomeTax = db_getItem(db, "salaryForm", "incomeTax", stoi(getItemID));
								string cleaningCost = db_getItem(db, "salaryForm", "cleaningCost", stoi(getItemID));
								string housingFund = db_getItem(db, "salaryForm", "housingFund", stoi(getItemID));
								tcpSend(sock, "[Get_Accepted] <itemID>" + getItemID + "</itemID> <basicSalary>" + basicSalary + "</basicSalary> <allowances>" + allowances + "</allowances> <livingAllowances>" + livingAllowances + "</livingAllowances> <phoneCost>" + phoneCost + "</phoneCost> <waterPowerCost>" + waterPowerCost + "</waterPowerCost> <houseRent>" + houseRent + "</houseRent> <incomeTax>" + incomeTax + "</incomeTax> <cleaningCost>" + cleaningCost + "</cleaningCost> <housingFund>" + housingFund + "</housingFund>");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get all values successfully.", 1);
							}
							else {
								tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
							}
						}
						else {
							if (db_getItem(db, "salaryForm", getValueType, stoi(getItemID)) != "") {
								string getValue = db_getItem(db, "salaryForm", getValueType, stoi(getItemID));
								tcpSend(sock, "[Get_Accepted] <value>" + getValue + "</value>");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get value successfully: " + getValue, 1);
							}
							else {
								tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
							}
						}
					}
					if (getTable == "teacherInfo") {
						if (getValueType == "all") {
							if (db_getItem(db, "teacherInfo", "teacherName", stoi(getItemID)) != "") {
								string teacherName = db_getItem(db, "teacherInfo", "teacherName", stoi(getItemID));
								string gender = db_getItem(db, "teacherInfo", "gender", stoi(getItemID));
								string companyName = db_getItem(db, "teacherInfo", "companyName", stoi(getItemID));
								string address = db_getItem(db, "teacherInfo", "address", stoi(getItemID));
								string tel = db_getItem(db, "teacherInfo", "tel", stoi(getItemID));
								tcpSend(sock, "[Get_Accepted] <itemID>" + getItemID + "</itemID> <teacherName>" + teacherName + "</teacherName> <gender>" + gender + "</gender> <companyName>" + companyName + "</companyName> <address>" + address + "</address> <tel>" + tel + "</tel>");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get all values successfully.", 1);
							}
							else {
								tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
							}
						}
						else {
							if (db_getItem(db, "teacherInfo", getValueType, stoi(getItemID)) != "") {
								string getValue = db_getItem(db, "teacherInfo", getValueType, stoi(getItemID));
								tcpSend(sock, "[Get_Accepted] <value>" + getValue + "</value>");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get value successfully: " + getValue, 1);
							}
							else {
								tcpSend(sock, "[Get_Rejected] ItemID does not exist.");
								postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2);
							}
						}

					}
					continue;
				}
				if (cmdType == "userSet") {
					int getUserID = stoi(getTextMiddle(argument, "<userID>", "</userID>"));
					string getToken = getTextMiddle(argument, "<token>", "</token> ");
					string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
					string getTable = getTextMiddle(argument, "<table>", "</table>");
					string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
					string getValueType = getTextMiddle(argument, "<valueType>", "</valueType>");
					string getValue = getTextMiddle(argument, "<value>", "</value>");
					if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "" || getValueType == "" || getValue == "") {
						tcpSend(sock, "[Set_Rejected] Invalid set information.");
						postLog("[Socket] Received invalid set request.", 2);
						continue;
					}
					if (!verifyTimestamp(getTimeStamp)) {
						tcpSend(sock, "[Set_Rejected] Invalid timestamp.");
						postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2);
						continue;
					}

					postLog("[Socket] Receive set request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID + ", valueType: " + getValueType, 1);
					if (getToken != userInfo.ID_TokenMap[getUserID]) {
						tcpSend(sock, "[Set_Rejected] Invalid token.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to invalid token.", 2);
						continue;
					}

					if (db_getItem(db, getTable, "teacherName", stoi(getItemID)) == "err: no specific id") {
						postLog("[Database] No such itemID[" + getItemID + "] in " + getTable + " table: " + getTable + ", create a new one.", 2);
						db_addItem(db, getTable, stoi(getItemID), "teacherID", getItemID);
//						for (int i = 0; i < MAX_USERS; i++) {
//							if (db_getItem(db, "salaryForm", "teacherName", i) == "err: no specific id") {
//								getItemID = i;
//								
//								break;
//							}
//							else {
//								tcpSend(sock, "[Set_Rejected] Database is full.");
//								postLog("[Database] salaryForm table is full, cannot add new item.", 2);
//								return;
//							}
//						}
					}
					if (db_updateItem(db, getTable, stoi(getItemID), getValueType, getValue)) {
						tcpSend(sock, "[Set_Accepted] Value set successfully.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " set value successfully: " + getValueType + " = " + getValue, 1);
						continue;
					}
					else {
						tcpSend(sock, "[Set_Rejected] Failed to set value.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " set value failed: " + getValueType + " = " + getValue, 2);
						continue;
					}
				}

				if (cmdType == "userDel") {
					int getUserID = stoi(getTextMiddle(argument, "<userID>", "</userID>"));
					string getToken = getTextMiddle(argument, "<token>", "</token> ");
					string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
					string getTable = getTextMiddle(argument, "<table>", "</table>");
					string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
					if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "") {
						tcpSend(sock, "[Del_Rejected] Invalid delete information.");
						postLog("[Socket] Received invalid delete request.", 2);
						continue;
					}
					if (!verifyTimestamp(getTimeStamp)) {
						tcpSend(sock, "[Del_Rejected] Invalid timestamp.");
						postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2);
						continue;
					}
					postLog("[Socket] Receive delete request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID, 1);
					if (getToken != userInfo.ID_TokenMap[getUserID]) {
						tcpSend(sock, "[Del_Rejected] Invalid token.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " delete rejected due to invalid token.", 2);
						continue;
					}
					if (db_rmItem(db, getTable, stoi(getItemID))) {
						tcpSend(sock, "[Del_Accepted] Item deleted successfully.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " deleted itemID " + getItemID + " successfully.", 1);
						continue;
					}
					else {
						tcpSend(sock, "[Del_Rejected] Failed to delete item.");
						postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " failed to delete itemID " + getItemID + ".", 2);
						continue;
					}
				}
				// Add cmdType handlers above this line
				tcpSend(sock, "[Error] Unknown command: " + cmdType);
				postLog("[Socket] Unknown command received: " + cmdType, 2);
				continue;
			}
			else {
				// Non-command payload; ignore but do not treat as disconnect.
				postLog("[Socket] Non-command data received from " + clientIP + ":" + to_string(clientPort) + " (" + to_string(bytesReceived) + " bytes).", 1);
				continue;
			}
		}
		else if (bytesReceived == 0) {
			// orderly shutdown by peer
			postLog("[Socket] Client closed connection (graceful): " + clientIP + ":" + to_string(clientPort), 0);
			break;
		}
		else { // bytesReceived == SOCKET_ERROR
			int err = WSAGetLastError();
			postLog("[Socket] recv() returned SOCKET_ERROR: " + to_string(err), 2);

			if (err == WSAEWOULDBLOCK) {
				// If using non-blocking sockets, wait with select() to avoid busy-looping.
				fd_set readfds;
				FD_ZERO(&readfds);
				FD_SET(sock, &readfds);
				timeval tv;
				tv.tv_sec = 5;
				tv.tv_usec = 0;
				int sel = select(0, &readfds, NULL, NULL, &tv);
				if (sel == 0) {
					continue;
				}
				if (sel == SOCKET_ERROR) {
					postLog("[Socket] select() failed: " + to_string(WSAGetLastError()), 2);
					break;
				}
				continue;
			}
			else if (err == WSAECONNRESET) {
				postLog("[Socket] Connection reset by peer: " + clientIP + ":" + to_string(clientPort), 2);
				break;
			}
			else {
				postLog("[Socket] recv() fatal error, closing connection: " + to_string(err), 2);
				break;
			}
		}
	}

	// Common cleanup path
	postLog("[Socket] Connection cleanup for " + clientIP + ":" + to_string(clientPort), 0);
	postLog("[Socket] userPortMap[" + to_string(clientPort) + "] = " + to_string(userInfo.port_IDMap[clientPort]), 0);
	if (userInfo.port_IDMap[clientPort] != 0) {
		postLog("[Socket] User [" + userInfo.name[userInfo.port_IDMap[clientPort]] + "] Exited without notification!", 2);
		userExit(userInfo.port_IDMap[clientPort], clientPort);
	}

	shutdown(sock, SD_BOTH);
	closesocket(sock);
}

int serverInit_Sockets() {
	if (inet_pton(AF_INET, ipAddr.server_cstr, &ipAddr.server) != 1) {
		postLog("[Socket] Failed to convert IP Address.", 4);
		return 1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		postLog("[Socket] WSAStartup failed.", 4);
		return 1;
	}

	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0); // Create main server socket
	if (listenSocket == INVALID_SOCKET) {
		postLog("[Socket] Failed to create socket.", 4);
		WSACleanup();
		return 1;
	}

	sockaddr_in serverAddr = {};
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr = ipAddr.server;
	serverAddr.sin_port = htons(ipAddr.listenPort);

	if (bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
		postLog("[Socket.service] Bind failed.", 4);
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
		postLog("[Socket] Listen failed.", 4);
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	// log the configured server IP string instead of unused server_vec
	postLog("[Socket] Server is listening on " + string(ipAddr.server_cstr) + ":" + to_string(ipAddr.listenPort) + ".", 1);
	postLog("[System] Initialization complete. Server is now running.", 1);

	// Accept loop: create and detach threads immediately
	while (true) {
		sockaddr_in clientAddr;
		int clientAddrSize = sizeof(clientAddr);
		SOCKET clientSocket_serviceSock = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrSize);
		if (clientSocket_serviceSock != INVALID_SOCKET) {
			globalSock.service = clientSocket_serviceSock;
			char ipStr[INET_ADDRSTRLEN];
			inet_ntop(AF_INET, &(clientAddr.sin_addr), ipStr, INET_ADDRSTRLEN);
			int clientPort = ntohs(clientAddr.sin_port);
			std::thread t(handle_client, clientSocket_serviceSock, string(ipStr), clientPort);
			t.detach(); // let thread run independently
		}
	}
}

void userExit(int userID, int clientPort) { // User logout processing function
	userInfo.name[userID] = "";
	userInfo.ID_PortMap[userID] = 0;
	userInfo.ID_TokenMap[userID] = "";
	userInfo.port_IDMap[clientPort] = 0;
}

string getTextMiddle(string source, string left, string right) { // Get content between left and right strings
	size_t leftPos = source.find(left);
	if (leftPos == string::npos) return "";
	leftPos += left.length();
	size_t rightPos = source.find(right, leftPos);
	if (rightPos == string::npos) return "";
	return source.substr(leftPos, rightPos - leftPos);
}

bool tcpSend(SOCKET sock, string str) {
	// Ensure the string is valid UTF-8 before sending
	string strToSend = ensureUtf8(str);
	if (strToSend != str) {
		postLog("[Encoding] Fixed UTF-8 encoding before sending", 1);
	}

	int r = send(sock, strToSend.c_str(), (int)strToSend.size(), 0);
	if (r == SOCKET_ERROR) {
		postLog("[method.tcpSend] send() failed: " + to_string(WSAGetLastError()), 4);
		return false;
	}
	return true;
}

bool verifyTimestamp(const string& timestampStr) { // Verify timestamp validity
	try {
		int timestamp = stoi(timestampStr);
		int currentTimestamp = static_cast<int>(time(nullptr));
		if (timestamp <= currentTimestamp + 300 && timestamp >= currentTimestamp - 300) {
			return true; // Timestamp within allowed range
		}
		else {
			return false; // Invalid timestamp
		}
	}
	catch (...) {
		return false; // Conversion failed, invalid timestamp
	}
}

int serverInit_SQLite_Open() {
	char* errMsg = nullptr;

	int if_db_opened = sqlite3_open(db_path.c_str(), &db);

	if (if_db_opened) {
		postLog("[SQLite] Failed to open database", 4);
		return if_db_opened;
	}

	postLog("[SQLite] Creating databases...", 1);
	const char* sql_create_users = "CREATE TABLE IF NOT EXISTS users (userId INT, userName TEXT, Passwd TEXT, PRIMARY KEY(userID, userName));";
	const char* sql_create_salaryForm = "CREATE TABLE IF NOT EXISTS salaryForm (teacherID INT , teacherName TEXT, basicSalary INT, allowances INT, livingAllowances INT, phoneCost INT, waterPowerCost INT, houseRent INT, incomeTax INT, cleaningCost INT, housingFund INT, PRIMARY KEY(teacherID, teacherName));";
	const char* sql_create_teacherInfo = "CREATE TABLE IF NOT EXISTS teacherInfo (teacherID INT , teacherName TEXT, gender TXT, companyName TXT, address TXT, tel TXT, PRIMARY KEY(teacherID, teacherName));";
	if (sqlite3_exec(db, sql_create_users, 0, 0, &errMsg) != SQLITE_OK) {
		postLog("[SQLite] SQL error (users): " + string(errMsg), 4);
		sqlite3_free(errMsg);
	}
	if (sqlite3_exec(db, sql_create_salaryForm, 0, 0, &errMsg) != SQLITE_OK) {
		postLog("[SQLite] SQL error (salaryForm): " + string(errMsg), 4);
		sqlite3_free(errMsg);
	}
	if (sqlite3_exec(db, sql_create_teacherInfo, 0, 0, &errMsg) != SQLITE_OK) {
		postLog("[SQLite] SQL error (teacherInfo): " + string(errMsg), 4);
		sqlite3_free(errMsg);
	}
}

string getRandomStr(const int len) { // Generate random string
	static const char charset[] =
		"0123456789"
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		"abcdefghijklmnopqrstuvwxyz";
	static thread_local mt19937 rg{ random_device{}() };
	static thread_local uniform_int_distribution<> pick(0, sizeof(charset) - 2);

	string s;
	s.reserve(len);
	for (int i = 0; i < len; ++i)
		s += charset[pick(rg)];
	return s;
}