import { addHours, differenceInSeconds } from "date-fns";
import net from "net";
import { timeZoneFix } from "../../../../globalUtils/timeZoneFix.js";
import { createLog } from "../../../logger/logger.js";
import { pausePrinter } from "../../utils/pausePrinter.js";
import { unPausePrinter } from "../../utils/unpausePrinter.js";

import { labelingProcess } from "../labeling/labelProcess.js";
import { autoLabelCreated } from "../labeling/labelRatio.js";
import { printerUpdate } from "./printerStatUpdate.js";

let logLevel: string = process.env.LOG_LEVEL || "info";
let errorCheck = false;
export const printerStatus = async (p: any) => {
	/**
	 * Checks each printer to see what the current state is
	 */
	createLog("debug", "ocp", "ocp", `Printer cycling`);

	const printer = new net.Socket();

	return new Promise((resolve) => {
		// connect to the printer, and check its status
		printer.connect(p.port, p.ipAddress, async () => {
			// write the message to the printer below gives us a feedback of the printer
			printer.write("~HS");
		});

		// read the data from the printer
		printer.on("data", async (data) => {
			const res = data.toString();

			// turn the data into an array to make it more easy to deal with
			const tmp = res.split(",");

			//--------------- time stuff-----------------------------------------------------------------
			// get last time printed
			const lastTime = new Date(p.lastTimePrinted).toISOString();
			// console.log(lastTime);

			// current time?

			/**
			 *
			 * add the time zone to the settings db
			 */
			// const currentTime = addHours(
			//     new Date(Date.now()),
			//     -6
			// ).toISOString();

			const currentTime = timeZoneFix();

			let timeBetween = 0;
			// if this is our first time printing pause the printer to start the timer, else we just update the time between timer
			if (lastTime === undefined) {
				printer.end();
				printerUpdate(p, 8);
				pausePrinter(p);
				resolve({ success: true, message: "First Time printing" });
			} else {
				timeBetween = differenceInSeconds(currentTime, lastTime);
			}

			// --- end time ---

			// --- printer logic ---
			createLog(
				"debug",
				"ocp",
				"ocp",
				`${p.name}: timeBetween: ${timeBetween}, delay ${parseInt(
					p.printDelay,
				)}, ${currentTime}... ${lastTime}`,
			);

			if (tmp[2] === "0" && tmp[4] !== "000") {
				// unpaused and printing labels - reset timer
				createLog(
					"info",
					"ocp",
					"ocp",
					`Unpaused and printing labels, time remaing ${differenceInSeconds(
						parseInt(p.printDelay),
						timeBetween,
					)}`,
				);

				// update last time printed in the array
				printerUpdate(p, 1);
			} else if (tmp[2] === "1" && tmp[4] !== "000") {
				// was paused or label sent from somewhere else
				createLog(
					"info",
					"ocp",
					"ocp",
					`${
						p.name
					} paused to soon, unpausing, remaining time: ${differenceInSeconds(
						parseInt(p.printDelay),
						timeBetween,
					)}`,
				);

				// reset the timer for this printer as well other labels shouldnt be sent but if we send them ok
				printerUpdate(p, 2);

				unPausePrinter(p);
			} else if (tmp[2] === "0" && timeBetween < parseInt(p.printDelay)) {
				// was unpaused to soon so repause it
				createLog(
					"info",
					"ocp",
					"ocp",
					`${p.name} Unpaused before the time allowed, time left ${
						differenceInSeconds(parseInt(p.printDelay), timeBetween) //seconds
					}`,
				);

				printerUpdate(p, 3);
				pausePrinter(p);
			} else if (tmp[2] === "0" && timeBetween > parseInt(p.printDelay)) {
				// its been long enough we can print a label
				createLog(
					"info",
					"ocp",
					"ocp",
					`${p.name} Allowed time passed and printing new label`,
				);

				// update last time printed in the array
				printerUpdate(p, 4);

				// sending over for labeling.
				labelingProcess({ printer: p });
				autoLabelCreated();
			} else if (tmp[2] === "0") {
				// printer was unpaused for the first time or made it here
				createLog("info", "ocp", "ocp", `${p.name} Frist time printing`);

				// add the time and printer
				printerUpdate(p, 4);

				// sending over for labeling.
				labelingProcess({ printer: p });
				autoLabelCreated();
			} else if (tmp[2] === "1") {
				// printer is paused and waiting
				createLog("debug", "ocp", "ocp", `${p.name} paused and waiting`);

				printerUpdate(p, 6);
			}

			printer.end();

			resolve({ success: true, message: "Print cycle completed." });
		});

		// as a safety destory it if its still there
		printer.on("end", () => {
			setTimeout(() => {
				if (!printer.destroyed) {
					createLog(
						"info",
						"printerState",
						"ocp",
						`${p.name}: was force closed, during normal cycle counting`,
					);
					printer.destroy();
				}
			}, 1000);
		});

		printer.on("error", async (error: any) => {
			// just going to say theres an error with the printer
			//console.log(error.code);
			if (error.code.includes("ETIMEDOUT") && !errorCheck) {
				createLog("error", "ocp", "ocp", `${p.name} is offline`);

				await printerUpdate(p, 9);
				errorCheck = true;
				printer.end();

				resolve({
					success: false,
					message: "The printer is offline.",
				});
			}
			if (!error.code.includes("ETIMEDOUT") && !errorCheck) {
				createLog(
					"error",
					"ocp",
					"ocp",
					`${p.name} encountered an error: ${error}`,
				);

				await printerUpdate(p, 7);
				errorCheck = true;

				// send log data
				// fake line
				printer.end();
				resolve({
					success: false,
					message: "There was an error with the printer.",
				});
			}
		});
	});
};

export const autoLabelingStats = async (p: any) => {
	/**
	 * Checks autolabeling printers just to see what they are doing.
	 */
	createLog("debug", "ocp", "ocp", `Printer cycling`);

	const printer = new net.Socket();

	return new Promise((resolve) => {
		// connect to the printer, and check its status
		printer.connect(p.port, p.ipAddress, async () => {
			// write the message to the printer below gives us a feedback of the printer
			printer.write("~HS");
		});

		// read the data from the printer
		printer.on("data", async (data) => {
			const res = data.toString();

			// turn the data into an array to make it more easy to deal with
			const tmp = res.split(",");

			if (tmp[4] !== "000") {
				// unpaused and printing labels - reset timer
				createLog("debug", "ocp", "ocp", `Printing Labels`);

				// update last time printed in the array
				printerUpdate(p, 1);
			}

			if (tmp[4] === "000") {
				// unpaused and printing labels - reset timer
				createLog("debug", "ocp", "ocp", `Printing Labels`);

				// update last time printed in the array
				printerUpdate(p, 5);
			}
		});

		printer.on("error", async (error) => {
			// just going to say theres an error with the printer
			console.log(error);
			if (!errorCheck) {
				createLog(
					"error",
					"ocp",
					"ocp",
					`${p.name}, encountered an error: ${error}`,
				);
			}

			await printerUpdate(p, 7);
			errorCheck = true;

			// send log data
			// fake line
			printer.end();
			resolve({
				success: false,
				message: "There was an error with the printer.",
			});
		});
	});
};
