import { createServer } from "node:http";
import os from "node:os";

import createApp from "./app.js";
import { db } from "./db/db.controller.js";
import { startDbNotificationListener } from "./db/db.listener.js";
import { setupDbNotifications } from "./db/db.setupNotifications.js";
import { dbCleanup } from "./db/dbCleanup.controller.js";
import { type Setting, settings } from "./db/schema/settings.schema.js";
import { connectGPSql } from "./gpSql/gpSqlConnection.controller.js";
import { createLogger } from "./logger/logger.controller.js";
import { historicalSchedule } from "./logistics/logistics.utils.historicalInv.js";
import { startNotifications } from "./notification/notification.controller.js";
import { sqlJobCleanUp } from "./notification/notification.SqlJobCleanUp.js";
import { createNotifications } from "./notification/notifications.master.js";
import { printerSync } from "./ocp/ocp.printer.manage.js";
import { monitorReleaseChanges } from "./opendock/openDockRreleaseMonitor.utils.js";
import { opendockSocketMonitor } from "./opendock/opendockSocketMonitor.utils.js";
import { connectProdSql } from "./prodSql/prodSqlConnection.controller.js";
import { monitorAlplaPurchase } from "./purchase/purchase.controller.js";
import { setupSocketIORoutes } from "./socket.io/serverSetup.js";
import { serversChecks } from "./system/serverData.controller.js";
import { baseSettingValidationCheck } from "./system/settingsBase.controller.js";
import { monitorProdAuditLog } from "./system/system.prodAuditLog.utils.js";
import { startTCPServer } from "./tcpServer/tcp.server.js";
import {
	aggregateRouteHitsForBusinessDay,
	cleanupOldRouteHits,
	runRouteHitAnalyticsCron,
} from "./utils/analyticRouteHits.utils.js";
import { createCronJob } from "./utils/croner.utils.js";
import { sendEmail } from "./utils/sendEmail.utils.js";
import { ppooMonitoring } from "./warehousing/warehousing.ppooMonitor.js";

const port = Number(process.env.PORT) || 3000;
export let systemSettings: Setting[] = [];

const start = async () => {
	const { app, baseUrl } = await createApp();

	const server = createServer(app);

	setupSocketIORoutes(baseUrl, server);

	const log = createLogger({ module: "system", subModule: "main start" });

	// triggering long lived processes
	startTCPServer();
	connectProdSql();
	connectGPSql();
	startDbNotificationListener();

	// trigger startup processes these must run before anything else can run
	await baseSettingValidationCheck();
	systemSettings = await db.select().from(settings);

	// when starting up long lived features the name must match the setting name.
	// also we always want to have long lived processes inside a setting check.
	setTimeout(() => {
		if (systemSettings.filter((n) => n.name === "opendock_sync")[0]?.active) {
			log.info({}, "Opendock is active");
			monitorReleaseChanges(); // this is od monitoring the db for all new releases
			opendockSocketMonitor();
			createCronJob("opendockAptCleanup", "0 30 5 * * *", () =>
				dbCleanup("opendockApt", 90),
			);
		}

		if (systemSettings.filter((n) => n.name === "purchaseMonitor")[0]?.active) {
			monitorAlplaPurchase();
		}

		if (systemSettings.filter((n) => n.name === "ocp")[0]?.active) {
			printerSync();
		}

		// these jobs below are system jobs and should run no matter what.
		createCronJob("JobAuditLogCleanUp", "0 0 5 * * *", () =>
			dbCleanup("jobs", 30),
		);
		createCronJob("logsCleanup", "0 15 5 * * *", () => dbCleanup("logs", 120));
		historicalSchedule();

		createCronJob("aggregateHits", "0 0 7 * * *", async () =>
			runRouteHitAnalyticsCron(),
		);

		createCronJob("ppooMonitor", "*/45 * * * * *", async () =>
			ppooMonitoring(),
		);

		createCronJob("cleanHitsUp", "0 0 7 * * *", () => cleanupOldRouteHits());
		createCronJob("ProdAuditLogMonitor", "*/30 * * * * *", () =>
			monitorProdAuditLog(),
		);
		// one shots only needed to run on server startups
		createNotifications();
		startNotifications();
		serversChecks();
		aggregateRouteHitsForBusinessDay();
		setupDbNotifications();

		// can be removed at a later date
		sqlJobCleanUp();
	}, 5 * 1000);

	process.on("uncaughtException", async (err) => {
		console.error("Uncaught Exception:", err);
		//await closePool();
		const emailData = {
			email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused.
			subject: `${os.hostname()} has just encountered a crash.`,
			template: "serverCrash",
			context: {
				error: err,
				plant: `${os.hostname()}`,
			},
		};

		await sendEmail(emailData);
		//process.exit(1);
	});

	server.listen(port, async () => {
		log.info(
			`Listening on http://${os.hostname()}:${port}${baseUrl}, logging in ${process.env.LOG_LEVEL}, current ENV ${process.env.NODE_ENV ? process.env.NODE_ENV : "development"}`,
		);
	});
};

start();
