import { ControllerManager } from "st-ethernet-ip";
import { createLog } from "../../../../logger/logger.js";
import { getMac } from "../../../utils/getMachineId.js";
import { labelingProcess } from "../../labeling/labelProcess.js";

export const createPlcMonitor = (config: any) => {
	let cm: any;
	let controllers: any = {};
	let stats: any = {};
	let isRunning = false;

	const nowISO = () => {
		return new Date().toISOString();
	};

	const start = () => {
		if (isRunning) return;

		cm = new ControllerManager();

		config.controllers.forEach((cfg: any) => {
			const plc: any = cm.addController(
				cfg.ip,
				cfg.slot,
				cfg.rpi,
				true,
				cfg.retrySP || 3000,
			);

			plc.connect();
			controllers[cfg.id] = plc;

			// initialize stats
			stats[cfg.id] = {
				id: cfg.id,
				ip: cfg.ip,
				slot: cfg.slot,
				scanRate: cfg.rpi,
				connected: false,
				lastConnectedAt: null,
				lastDisconnectedAt: null,
				reconnectCount: 0,
			};
			// Add tags
			cfg.tags.forEach((tag: any) => plc.addTag(tag));

			// Events
			plc.on("Connected", () => {
				const s = stats[cfg.id];
				s.connected = true;
				s.lastConnectedAt = nowISO();
				if (s.lastDisconnectedAt) {
					s.reconnectCount++;
				}
				createLog(
					"info",
					"zechette",
					"ocp",
					`[${cfg.id}] Connected @ ${cfg.ip}:${cfg.slot}`,
				);
			});

			plc.on("Disconnected", () => {
				const s = stats[cfg.id];
				s.connected = false;
				s.lastDisconnectedAt = nowISO();
				createLog("info", "zechette", "ocp", `[${cfg.id}] Disconnected`);
			});

			plc.on("error", (err: any) => {
				createLog(
					"error",
					"zechette",
					"ocp",
					`[${cfg.id}] Error: ${JSON.stringify(err.message)}`,
				);
			});

			plc.on("TagChanged", async (tag: any, prevVal: any) => {
				if (tag.value !== 0) {
					const time = nowISO();
					if (tag.value === 0) return;
					setTimeout(async () => {
						if (tag.value === 0) return;
						const macId = await getMac(tag.value);
						// const { data, error } = (await tryCatch(
						//     query(
						//         getCurrentLabel
						//             .replace(
						//                 "[macId]",
						//                 macId[0]?.HumanReadableId
						//             )
						//             .replace(
						//                 "[time]",
						//                 format(time, "yyyy-MM-dd HH:mm")
						//             ),
						//         "Current label data"
						//     )
						// )) as any;

						// createLog(
						//     "info",
						//     "zechettii",
						//     "zechettii",
						//     `${format(time, "yyyy-MM-dd HH:mm")} [${cfg.id}] ${
						//         tag.name
						//     }: ${prevVal} -> ${
						//         tag.value
						//     }, the running number is ${
						//         error ? null : data.data[0]?.LfdNr
						//     }}`
						// );
						const zechette = {
							line: tag.value.toString(),
							printer: cfg.printerId, // this is the id of the zechetti 2 to print we should move this to the db
							printerName: cfg.id,
						};
						createLog(
							"info",
							"zechette",
							"ocp",
							`Date being sent to labeler: ${JSON.stringify(zechette)}`,
						);
						labelingProcess({ zechette: zechette });
					}, 150);
				}
			});
		});

		isRunning = true;
	};

	const stop = () => {
		if (!isRunning) return;
		Object.values(controllers).forEach((plc: any) => {
			try {
				plc.disconnect();
			} catch {}
		});
		controllers = {};
		cm = null;
		isRunning = false;
		console.log("Monitor stopped");
	};

	const restart = () => {
		console.log("Restarting the plc(s)");
		stop();
		new Promise((resolve) => setTimeout(resolve, 1500));
		start();
	};

	const status = () => {
		const result: any = {};

		for (const [id, s] of Object.entries(stats)) {
			let s: any;
			let uptimeMs = null,
				downtimeMs = null;
			if (s.connected && s.lastConnectedAt) {
				uptimeMs = Date.now() - new Date(s.lastConnectedAt).getTime();
			} else if (!s.connected && s.lastDisconnectedAt) {
				downtimeMs = Date.now() - new Date(s.lastDisconnectedAt).getTime();
			}
			result[id] = { ...s, uptimeMs, downtimeMs };
		}
		return result;
	};

	return { start, stop, restart, status };
};
