'use strict';
/**
 * Remote diagnostics — a FIXED, allowlisted suite. No arbitrary code/command execution: the plane can
 * only ask "run diagnostics", and we run exactly these checks (connectivity, DNS, disk/mem, log tail).
 */
const os = require('os');
const dns = require('dns');
const http = require('http');
const https = require('https');
const { URL } = require('url');

/** HEAD/GET a URL and report reachability + status + latency. Never follows into anything unexpected. */
function probe(method, target, timeoutMs = 8000) {
	return new Promise((resolve) => {
		let u;
		try {
			u = new URL(target);
		} catch (e) {
			resolve({ ok: false, error: 'bad url' });
			return;
		}
		const mod = u.protocol === 'https:' ? https : http;
		const started = Date.now();
		const req = mod.request(u, { method, timeout: timeoutMs }, (res) => {
			res.resume();
			resolve({ ok: res.statusCode > 0 && res.statusCode < 500, status: res.statusCode, ms: Date.now() - started });
		});
		req.on('timeout', () => {
			req.destroy();
			resolve({ ok: false, error: 'timeout' });
		});
		req.on('error', (e) => resolve({ ok: false, error: e.message }));
		req.end();
	});
}

function resolveDns(host, timeoutMs = 5000) {
	return new Promise((resolve) => {
		let done = false;
		const t = setTimeout(() => {
			if (!done) {
				done = true;
				resolve({ ok: false, error: 'timeout' });
			}
		}, timeoutMs);
		dns.lookup(host, (err, address) => {
			if (done) return;
			done = true;
			clearTimeout(t);
			resolve(err ? { ok: false, error: err.message } : { ok: true, address });
		});
	});
}

const hostOf = (u) => {
	try {
		return new URL(u).host;
	} catch (e) {
		return null;
	}
};

/**
 * Run the suite. ctx = { centralUrl, smilUrl, wsConnected, logTail() }.
 * Returns a structured result (serializable) for the plane.
 */
async function run(ctx) {
	const c = ctx || {};
	const result = {
		at: new Date().toISOString(),
		hostname: os.hostname(),
		uptimeSec: Math.round(os.uptime()),
		memory: { totalMb: Math.round(os.totalmem() / 1048576), freeMb: Math.round(os.freemem() / 1048576) },
		loadavg: os.loadavg ? os.loadavg().map((n) => Number(n.toFixed(2))) : null,
		ws: { connected: !!c.wsConnected },
		checks: {},
	};
	if (c.centralUrl) {
		const base = c.centralUrl.replace(/\/+$/, '');
		result.checks.controlPlane = await probe('GET', base + '/health').then((r) =>
			r.ok || r.status ? r : probe('GET', base), // fall back to base if /health absent
		);
		const ch = hostOf(c.centralUrl);
		if (ch) result.checks.controlPlaneDns = await resolveDns(ch.split(':')[0]);
	}
	if (c.smilUrl) {
		result.checks.tenantSmil = await probe('HEAD', c.smilUrl);
		const th = hostOf(c.smilUrl);
		if (th) result.checks.tenantDns = await resolveDns(th.split(':')[0]);
	}
	if (typeof c.logTail === 'function') {
		try {
			result.logTail = c.logTail();
		} catch (e) {
			/* ignore */
		}
	}
	return result;
}

module.exports = { run };
