import TcpSocket from "react-native-tcp-socket";

// const STX = "\x02";
// const ETX = "\x03";

type TcpResponse = {
	success: boolean;
	message: string;
	data: ScannerEvent;
};

type ScannerEvent = {
	scannerId?: string;
	commandDescription?: string;
	prompt?: string;
	message?: string;
	status: "success" | "error" | "location" | "unknown" | "scan";
	lines?: string[];
};

// const ERROR_MESSAGES = [
// 	"Invalid barcode",
// 	"Already scanned",
// 	"Not on stock",
// 	"Article tolerance for consolidation not satisfied.",
// ];

const ERROR_KEYWORDS = [
	"invalid barcode",
	"invalid command",
	"already",
	"not on stock",
	"article tolerance",
	"unloaded",
	"delivered",
	"blocked",
	"not possible",
	"forbidden",
	"loading not",
	"not",
	"error",
	"already",
	"double",
	"no",
	"bad",
];

// function parseErpResponse(buffer: Buffer) {
// 	const text = buffer
// 		.toString("utf8")
// 		.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|#[0-9A-Za-z])/g, "")
// 		.replace(/\x02/g, "")
// 		.replace(/\x03/g, "")
// 		.trim();

// 	const noHeader = text.replace(/^\d+@/, "");
// 	console.log(text);
// 	if (!noHeader.includes("Scan:")) {
// 		return {
// 			raw: text,
// 			type: "error",
// 			message: noHeader.trim(),
// 			lines: [noHeader.trim()],
// 		};
// 	}

// 	const [actionPart, scanPart = ""] = noHeader.split("Scan:");
// 	const action = actionPart.trim();
// 	const scanClean = scanPart.trim();

// 	const successMatch = scanClean.match(/^(.*?)\s+V$/);

// 	if (successMatch) {
// 		const prompt = successMatch[1].trim();

// 		return {
// 			raw: text,
// 			type: "success",
// 			action,
// 			prompt,
// 			status: "V",
// 			lines: [action, prompt, "V"],
// 		};
// 	}

// 	// // Handles: "Production lotInvalid barcode"
// 	// const knownErrors = [
// 	// 	"Invalid barcode",
// 	// 	"Invalid machine",
// 	// 	"Not on stock",
// 	// 	"Article tolerance for consolidation not satisfied",
// 	// ].sort((a, b) => b.length - a.length);

// 	// const foundError = knownErrors.find((err) => scanClean.includes(err));

// 	// if (foundError) {
// 	// 	const prompt = scanClean.replace(foundError, "").trim();

// 	// 	return {
// 	// 		raw: text,
// 	// 		type: "error",
// 	// 		action,
// 	// 		prompt,
// 	// 		message: foundError,
// 	// 		lines: [action, prompt, foundError].filter(Boolean),
// 	// 	};
// 	// }

// 	// return {
// 	// 	raw: text,
// 	// 	type: "pending",
// 	// 	action,
// 	// 	prompt: scanClean,
// 	// 	lines: [action, scanClean].filter(Boolean),
// 	// };

// 	const unitMatch = scanClean.match(/^(Unit\s+\d+\/\d+)(.*)$/);

// 	if (unitMatch) {
// 		const prompt = unitMatch[1].trim(); // "Unit 1/4"
// 		const remainder = unitMatch[2].trim(); // everything after

// 		// SUCCESS
// 		if (remainder === "V") {
// 			return {
// 				raw: text,
// 				type: "success",
// 				action,
// 				prompt,
// 				status: "V",
// 				lines: [action, prompt, "V"],
// 			};
// 		}

// 		// Known ERP errors
// 		const knownErrors = [
// 			"Invalid barcode",
// 			"Invalid machine",
// 			"Not on stock",
// 			"Article tolerance for consolidation not satisfied",
// 		];

// 		const foundError = knownErrors.find((err) =>
// 			remainder.toLowerCase().includes(err.toLowerCase()),
// 		);

// 		if (foundError) {
// 			return {
// 				raw: text,
// 				type: "error",
// 				action,
// 				prompt,
// 				message: foundError,
// 				lines: [action, prompt, foundError],
// 			};
// 		}

// 		if (remainder) {
// 			return {
// 				raw: text,
// 				type: "prompt",
// 				action,
// 				prompt,
// 				message: remainder,
// 				lines: [action, prompt, remainder],
// 			};
// 		}

// 		return {
// 			raw: text,
// 			type: "pending",
// 			action,
// 			prompt,
// 			lines: [action, prompt],
// 		};
// 	}
// }

const parseScannerText = (buffer: Buffer) => {
	const text = buffer.toString("utf8");

	return (
		text
			// remove cursor movement like ESC[122C, ESC[2;1H, ESC[8q
			.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "\n")

			// remove other ANSI sequences like ESC#5
			.replace(/\x1B#[0-9]/g, "\n")

			// normalize carriage returns
			.replace(/\r/g, "\n")

			// split into clean lines
			.split(/\n+/)

			// clean each line
			.map((line) => line.trim())

			// remove blanks
			.filter(Boolean)
	);
};

const parseScannerEvent = (lines: string[]): ScannerEvent => {
	const scannerId = lines[0];
	const messageLines = lines.slice(1);
	const message = messageLines.at(-1);

	const commandDescription = messageLines.find((x) => /^\d+\s+/.test(x));
	const prompt = messageLines.find((x) => /^Scan:/i.test(x));

	let status: ScannerEvent["status"] = "unknown";

	const msg = message?.toLowerCase() ?? "";

	if (msg === "v") status = "success";
	else if (msg && ERROR_KEYWORDS.some((keyword) => msg.includes(keyword)))
		status = "error";
	else if (msg?.includes("scan")) status = "success";
	// everything else will just be a location
	else if (commandDescription?.includes("Relocate")) status = "location";

	// TODO: split command description and use the command id next to description for sorting.
	return {
		scannerId,
		commandDescription,
		prompt,
		message,
		status,
		lines,
	};
};

/**
 * Sends a Zebra-style TCP message:
 * <STX>98@{scanned}<ETX>
 */
export async function sendTcpMessage(
	command: string,
	host: string,
	port: number,
	timeoutMs = 5000,
): Promise<TcpResponse> {
	return new Promise((resolve) => {
		//const responses: any = [];

		const client = TcpSocket.createConnection({ host, port }, () => {
			//console.log("Sending TCP (visible):", `${command}`);

			client.write(command);
		});

		let settled = false;

		const done = (response: TcpResponse) => {
			if (settled) return;

			settled = true;

			clearTimeout(timeout);

			try {
				client.destroy();
			} catch {}

			resolve(response);
		};

		// const timeout = setTimeout(() => {
		// 	client.destroy();

		// 	resolve({
		// 		success: false,
		// 		message: "TCP timeout",
		// 		data: responses,
		// 	});
		// }, timeoutMs);

		const timeout = setTimeout(() => {
			done({
				success: false,
				message: "No response from scanner server",
				data: {
					scannerId: "999",
					commandDescription: "TCP Command",
					prompt: command,
					message: "Invalid command",
					status: "error",
					lines: [
						"SYSTEM",
						"TCP Command",
						`Scan: ${command}`,
						"Invalid command",
					],
				},
			});
		}, timeoutMs);

		client.on("data", (data: any) => {
			//console.log("TCP received:", text);
			const parsed = parseScannerText(data);
			//console.log("scanned:", parsed);

			//responses.push(parsed);

			const cleaned = parseScannerEvent(parsed);

			//console.log(responses);
			// clearTimeout(timeout);
			// resolve({
			// 	success: true,
			// 	message: "TCP Response",
			// 	data: cleaned as any,
			// });

			done({
				success: cleaned.status !== "error",
				message:
					cleaned.status === "error"
						? (cleaned.message ?? "TCP Error")
						: "TCP Response",
				data: cleaned,
			});
		});

		client.on("error", (err) => {
			// resolve({
			// 	success: false,
			// 	message: err.message,
			// 	data: ["Error", "Please try again"] as any,
			// });

			done({
				success: false,
				message: err.message,
				data: {
					scannerId: "SYSTEM",
					commandDescription: "TCP Error",
					prompt: command,
					message: err.message,
					status: "error",
					lines: ["SYSTEM", "TCP Error", `Scan: ${command}`, err.message],
				},
			});
		});

		client.on("close", () => {
			if (settled) return;

			// resolve({
			// 	success: true,
			// 	message: "TCP complete",
			// 	data: ["Error", "Please try again"] as any,
			// });

			done({
				success: false,
				message: "TCP connection closed",
				data: {
					scannerId: "SYSTEM",
					commandDescription: "TCP Closed",
					prompt: command,
					message: "Connection closed before response",
					status: "error",
					lines: [
						"SYSTEM",
						"TCP Closed",
						`Scan: ${command}`,
						"Connection closed before response",
					],
				},
			});
		});
	});
}
