import "node:module";
import stringHelpers from "@poppinss/utils/string";
import { debuglog } from "node:util";
import diagnostics_channel from "node:diagnostics_channel";
import os from "node:os";
import v8 from "node:v8";
import checkDiskSpace from "check-disk-space";
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
	let target = {};
	for (var name in all) __defProp(target, name, {
		get: all[name],
		enumerable: true
	});
	if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
	return target;
};
var Result = class Result {
	static ok(message) {
		return new Result(message, "ok", /* @__PURE__ */ new Date());
	}
	static failed(message, error) {
		const result = new Result(typeof message === "string" ? message : message.message, "error", /* @__PURE__ */ new Date());
		if (error) result.setMetaData({ error });
		if (typeof message !== "string") result.setMetaData({ error: message });
		return result;
	}
	static warning(message) {
		return new Result(message, "warning", /* @__PURE__ */ new Date());
	}
	constructor(message, status, finishedAt) {
		this.message = message;
		this.status = status;
		this.finishedAt = finishedAt;
	}
	setFinishedAt(finishedAt) {
		this.finishedAt = finishedAt;
		return this;
	}
	setMetaData(metaData) {
		this.meta = metaData;
		return this;
	}
	mergeMetaData(metaData) {
		this.meta = {
			...this.meta,
			...metaData
		};
		return this;
	}
	toJSON() {
		return {
			finishedAt: this.finishedAt,
			message: this.message,
			status: this.status,
			...this.meta ? { meta: this.meta } : {}
		};
	}
};
var BaseCheck = class {
	as(name) {
		this.name = name;
		return this;
	}
	cacheFor(duration) {
		this.cacheDuration = stringHelpers.seconds.parse(duration);
		return this;
	}
};
var debug_default = debuglog("adonisjs:health");
var tracing_channels_exports = /* @__PURE__ */ __exportAll({ healthCheck: () => healthCheck });
const healthCheck = diagnostics_channel.tracingChannel("adonisjs.health.check");
var HealthChecks = class {
	#checks = [];
	#cachedResults = /* @__PURE__ */ new Map();
	#getDebugInfo() {
		return {
			pid: process.pid,
			ppid: process.ppid,
			platform: process.platform,
			uptime: process.uptime(),
			version: process.version
		};
	}
	async #runCheck(check) {
		if (check.cacheDuration) {
			const cachedResult = this.#cachedResults.get(check.name);
			const cacheMilliseconds = Math.floor(check.cacheDuration * 1e3);
			if (cachedResult && Date.now() < cachedResult.finishedAt.getTime() + cacheMilliseconds) {
				debug_default("returning cached results for \"%s\" check", check.name, cachedResult);
				return {
					name: check.name,
					isCached: true,
					...cachedResult
				};
			}
			const result = await healthCheck.tracePromise(check.run, healthCheck.hasSubscribers ? { check } : void 0, check);
			debug_default("executed \"%s\" check", check.name, result);
			this.#cachedResults.set(check.name, result);
			return {
				name: check.name,
				isCached: false,
				...result
			};
		}
		const result = await healthCheck.tracePromise(check.run, healthCheck.hasSubscribers ? { check } : void 0, check);
		debug_default("executed \"%s\" check", check.name, result);
		return {
			name: check.name,
			isCached: false,
			...result
		};
	}
	register(checks) {
		this.#checks = checks;
		return this;
	}
	append(checks) {
		this.#checks = this.#checks.concat(checks);
		return this;
	}
	async run() {
		let isHealthy = true;
		let status = "ok";
		const checks = await Promise.all(this.#checks.map(async (check) => {
			const result = await this.#runCheck(check);
			if (result.status === "error") {
				status = "error";
				isHealthy = false;
			} else if (status === "ok" && result.status === "warning") status = "warning";
			return result;
		}));
		return {
			isHealthy,
			status,
			finishedAt: /* @__PURE__ */ new Date(),
			debugInfo: this.#getDebugInfo(),
			checks
		};
	}
};
var MemoryRSSCheck = class extends BaseCheck {
	#warnThresholdBytes = stringHelpers.bytes.parse("320 mb");
	#failThresholdBytes = stringHelpers.bytes.parse("350 mb");
	#warnThresholdPercentage = null;
	#failThresholdPercentage = null;
	#computeFn = () => {
		return process.memoryUsage();
	};
	name = "Memory RSS check";
	warnWhenExceeds(value) {
		const parsedValue = stringHelpers.bytes.parse(value);
		if (parsedValue === null) throw new Error(`Invalid byte value for warnWhenExceeds: ${value}`);
		this.#warnThresholdBytes = parsedValue;
		this.#warnThresholdPercentage = null;
		return this;
	}
	failWhenExceeds(value) {
		const parsedValue = stringHelpers.bytes.parse(value);
		if (parsedValue === null) throw new Error(`Invalid byte value for failWhenExceeds: ${value}`);
		this.#failThresholdBytes = parsedValue;
		this.#failThresholdPercentage = null;
		return this;
	}
	warnWhenExceedsPercentage(valueInPercentage) {
		if (valueInPercentage < 0 || valueInPercentage > 100) throw new Error("Warn threshold percentage must be between 0 and 100.");
		this.#warnThresholdPercentage = valueInPercentage;
		this.#warnThresholdBytes = null;
		return this;
	}
	failWhenExceedsPercentage(valueInPercentage) {
		if (valueInPercentage < 0 || valueInPercentage > 100) throw new Error("Fail threshold percentage must be between 0 and 100.");
		this.#failThresholdPercentage = valueInPercentage;
		this.#failThresholdBytes = null;
		return this;
	}
	compute(callback) {
		this.#computeFn = callback;
		return this;
	}
	async run() {
		const { rss } = this.#computeFn();
		const isWarningThresholdPercentageSet = this.#warnThresholdPercentage !== null;
		const isFailureThresholdPercentageSet = this.#failThresholdPercentage !== null;
		const compareAsPercentage = isWarningThresholdPercentageSet || isFailureThresholdPercentageSet;
		let actualWarnThreshold;
		let actualFailThreshold;
		let valueToCompare;
		let totalSystemMemory = null;
		let usedPercentage = null;
		if (compareAsPercentage) {
			totalSystemMemory = os.totalmem();
			if (totalSystemMemory === 0) return Result.failed("Cannot determine total system memory (0 bytes).").mergeMetaData({ memoryInBytes: {
				used: rss,
				totalSystemMemory
			} });
			usedPercentage = Math.floor(rss / totalSystemMemory * 100);
			valueToCompare = usedPercentage;
			if (this.#warnThresholdPercentage !== null) actualWarnThreshold = this.#warnThresholdPercentage;
			else if (this.#warnThresholdBytes !== null) actualWarnThreshold = Math.floor(this.#warnThresholdBytes / totalSystemMemory * 100);
			else return Result.failed("Warning threshold for percentage comparison is missing.").mergeMetaData({});
			if (this.#failThresholdPercentage !== null) actualFailThreshold = this.#failThresholdPercentage;
			else if (this.#failThresholdBytes !== null) actualFailThreshold = Math.floor(this.#failThresholdBytes / totalSystemMemory * 100);
			else return Result.failed("Failure threshold for percentage comparison is missing.").mergeMetaData({});
		} else {
			valueToCompare = rss;
			if (this.#warnThresholdBytes === null) return Result.failed("Warning threshold (bytes) is missing.").mergeMetaData({});
			actualWarnThreshold = this.#warnThresholdBytes;
			if (this.#failThresholdBytes === null) return Result.failed("Failure threshold (bytes) is missing.").mergeMetaData({});
			actualFailThreshold = this.#failThresholdBytes;
		}
		const metaData = {};
		if (compareAsPercentage) {
			metaData.sizeInPercentage = {
				used: usedPercentage,
				failureThreshold: actualFailThreshold,
				warningThreshold: actualWarnThreshold
			};
			if (totalSystemMemory !== null) metaData.memoryInBytes = {
				used: rss,
				totalSystemMemory,
				failureThreshold: Math.floor(actualFailThreshold / 100 * totalSystemMemory),
				warningThreshold: Math.floor(actualWarnThreshold / 100 * totalSystemMemory)
			};
		} else metaData.memoryInBytes = {
			used: rss,
			failureThreshold: actualFailThreshold,
			warningThreshold: actualWarnThreshold
		};
		if (valueToCompare >= actualFailThreshold) {
			const formattedUsed = compareAsPercentage ? `${valueToCompare}%` : stringHelpers.bytes.format(rss);
			const formattedThreshold = compareAsPercentage ? `${actualFailThreshold}%` : stringHelpers.bytes.format(actualFailThreshold);
			return Result.failed(`RSS usage is ${formattedUsed}, which is above the threshold of ${formattedThreshold}`).mergeMetaData(metaData);
		}
		if (valueToCompare >= actualWarnThreshold) {
			const formattedUsed = compareAsPercentage ? `${valueToCompare}%` : stringHelpers.bytes.format(rss);
			const formattedThreshold = compareAsPercentage ? `${actualWarnThreshold}%` : stringHelpers.bytes.format(actualWarnThreshold);
			return Result.warning(`RSS usage is ${formattedUsed}, which is above the threshold of ${formattedThreshold}`).mergeMetaData(metaData);
		}
		return Result.ok("RSS usage is under defined thresholds").mergeMetaData(metaData);
	}
};
var MemoryHeapCheck = class extends BaseCheck {
	#warnThresholdBytes = stringHelpers.bytes.parse("250 mb");
	#failThresholdBytes = stringHelpers.bytes.parse("300 mb");
	#warnThresholdPercentage = null;
	#failThresholdPercentage = null;
	#computeFn = () => {
		return process.memoryUsage();
	};
	name = "Memory heap check";
	warnWhenExceeds(value) {
		const parsedValue = stringHelpers.bytes.parse(value);
		if (parsedValue === null) throw new Error(`Invalid byte value for warnWhenExceeds: ${value}`);
		this.#warnThresholdBytes = parsedValue;
		this.#warnThresholdPercentage = null;
		return this;
	}
	failWhenExceeds(value) {
		const parsedValue = stringHelpers.bytes.parse(value);
		if (parsedValue === null) throw new Error(`Invalid byte value for failWhenExceeds: ${value}`);
		this.#failThresholdBytes = parsedValue;
		this.#failThresholdPercentage = null;
		return this;
	}
	warnWhenExceedsPercentage(valueInPercentage) {
		if (valueInPercentage < 0 || valueInPercentage > 100) throw new Error("Warn threshold percentage must be between 0 and 100.");
		this.#warnThresholdPercentage = valueInPercentage;
		this.#warnThresholdBytes = null;
		return this;
	}
	failWhenExceedsPercentage(valueInPercentage) {
		if (valueInPercentage < 0 || valueInPercentage > 100) throw new Error("Fail threshold percentage must be between 0 and 100.");
		this.#failThresholdPercentage = valueInPercentage;
		this.#failThresholdBytes = null;
		return this;
	}
	compute(callback) {
		this.#computeFn = callback;
		return this;
	}
	async run() {
		const { heapUsed } = this.#computeFn();
		const isWarningThresholdPercentageSet = this.#warnThresholdPercentage !== null;
		const isFailureThresholdPercentageSet = this.#failThresholdPercentage !== null;
		const compareAsPercentage = isWarningThresholdPercentageSet || isFailureThresholdPercentageSet;
		let actualWarnThreshold;
		let actualFailThreshold;
		let valueToCompare;
		let maxHeapSize = null;
		let usedPercentage = null;
		if (compareAsPercentage) {
			maxHeapSize = v8.getHeapStatistics().heap_size_limit;
			usedPercentage = Math.floor(heapUsed / maxHeapSize * 100);
			valueToCompare = usedPercentage;
			if (this.#warnThresholdPercentage !== null) actualWarnThreshold = this.#warnThresholdPercentage;
			else if (this.#warnThresholdBytes !== null) actualWarnThreshold = Math.floor(this.#warnThresholdBytes / maxHeapSize * 100);
			else return Result.failed("Warning threshold for percentage comparison is missing.").mergeMetaData({});
			if (this.#failThresholdPercentage !== null) actualFailThreshold = this.#failThresholdPercentage;
			else if (this.#failThresholdBytes !== null) actualFailThreshold = Math.floor(this.#failThresholdBytes / maxHeapSize * 100);
			else return Result.failed("Failure threshold for percentage comparison is missing.").mergeMetaData({});
		} else {
			valueToCompare = heapUsed;
			if (this.#warnThresholdBytes === null) return Result.failed("Warning threshold (bytes) is missing.").mergeMetaData({});
			actualWarnThreshold = this.#warnThresholdBytes;
			if (this.#failThresholdBytes === null) return Result.failed("Failure threshold (bytes) is missing.").mergeMetaData({});
			actualFailThreshold = this.#failThresholdBytes;
		}
		const metaData = {};
		if (compareAsPercentage) {
			metaData.sizeInPercentage = {
				used: usedPercentage,
				failureThreshold: actualFailThreshold,
				warningThreshold: actualWarnThreshold
			};
			if (maxHeapSize !== null) metaData.heapInBytes = {
				used: heapUsed,
				maxHeapSize,
				failureThreshold: Math.floor(actualFailThreshold / 100 * maxHeapSize),
				warningThreshold: Math.floor(actualWarnThreshold / 100 * maxHeapSize)
			};
		} else metaData.memoryInBytes = {
			used: heapUsed,
			failureThreshold: actualFailThreshold,
			warningThreshold: actualWarnThreshold
		};
		if (valueToCompare >= actualFailThreshold) {
			const formattedUsed = compareAsPercentage ? `${valueToCompare}%` : stringHelpers.bytes.format(heapUsed);
			const formattedThreshold = compareAsPercentage ? `${actualFailThreshold}%` : stringHelpers.bytes.format(actualFailThreshold);
			return Result.failed(`Heap usage is ${formattedUsed}, which is above the threshold of ${formattedThreshold}`).mergeMetaData(metaData);
		}
		if (valueToCompare >= actualWarnThreshold) {
			const formattedUsed = compareAsPercentage ? `${valueToCompare}%` : stringHelpers.bytes.format(heapUsed);
			const formattedThreshold = compareAsPercentage ? `${actualWarnThreshold}%` : stringHelpers.bytes.format(actualWarnThreshold);
			return Result.warning(`Heap usage is ${formattedUsed}, which is above the threshold of ${formattedThreshold}`).mergeMetaData(metaData);
		}
		return Result.ok("Heap usage is under defined thresholds").mergeMetaData(metaData);
	}
};
var DiskSpaceCheck = class extends BaseCheck {
	#warnThreshold = 75;
	#failThreshold = 80;
	#computeFn = () => {
		return checkDiskSpace(this.diskPath);
	};
	name = "Disk space check";
	diskPath = process.platform === "win32" ? "C:\\" : "/";
	warnWhenExceeds(valueInPercentage) {
		this.#warnThreshold = valueInPercentage;
		return this;
	}
	failWhenExceeds(valueInPercentage) {
		this.#failThreshold = valueInPercentage;
		return this;
	}
	compute(callback) {
		this.#computeFn = callback;
		return this;
	}
	async run() {
		const { free, size } = await this.#computeFn();
		const usedPercentage = Math.floor((size - free) / size * 100);
		const metaData = { sizeInPercentage: {
			used: usedPercentage,
			failureThreshold: this.#failThreshold,
			warningThreshold: this.#warnThreshold
		} };
		if (usedPercentage >= this.#failThreshold) return Result.failed(`Disk usage is ${usedPercentage}%, which is above the threshold of ${this.#failThreshold}%`).mergeMetaData(metaData);
		if (usedPercentage >= this.#warnThreshold) return Result.warning(`Disk usage is ${usedPercentage}%, which is above the threshold of ${this.#warnThreshold}%`).mergeMetaData(metaData);
		return Result.ok("Disk usage is under defined thresholds").mergeMetaData(metaData);
	}
};
export { BaseCheck, DiskSpaceCheck, HealthChecks, MemoryHeapCheck, MemoryRSSCheck, Result, tracing_channels_exports as tracingChannels };
