import { i as CONFIG_DIR, u as ensureDir } from "./agent-paths-DUHGyU1s.js";
import os from "node:os";
import path from "node:path";
import fs from "node:fs";

//#region src/infra/widearea-dns.ts
function normalizeWideAreaDomain(raw) {
	const trimmed = raw?.trim();
	if (!trimmed) return null;
	return trimmed.endsWith(".") ? trimmed : `${trimmed}.`;
}
function resolveWideAreaDiscoveryDomain(params) {
	const env = params?.env ?? process.env;
	return normalizeWideAreaDomain(params?.configDomain ?? env.FASED_WIDE_AREA_DOMAIN ?? null);
}
function zoneFilenameForDomain(domain) {
	return `${domain.replace(/\.$/, "")}.db`;
}
function getWideAreaZonePath(domain) {
	return path.join(CONFIG_DIR, "dns", zoneFilenameForDomain(domain));
}
function dnsLabel(raw, fallback) {
	const normalized = raw.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
	const out = normalized.length > 0 ? normalized : fallback;
	return out.length <= 63 ? out : out.slice(0, 63);
}
function txtQuote(value) {
	return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n")}"`;
}
function formatYyyyMmDd(date) {
	return `${date.getUTCFullYear()}${String(date.getUTCMonth() + 1).padStart(2, "0")}${String(date.getUTCDate()).padStart(2, "0")}`;
}
function nextSerial(existingSerial, now) {
	const today = formatYyyyMmDd(now);
	const base = Number.parseInt(`${today}01`, 10);
	if (!existingSerial || !Number.isFinite(existingSerial)) return base;
	if (String(existingSerial).startsWith(today)) return existingSerial + 1;
	return base;
}
function extractSerial(zoneText) {
	const match = zoneText.match(/^\s*@\s+IN\s+SOA\s+\S+\s+\S+\s+(\d+)\s+/m);
	if (!match) return null;
	const parsed = Number.parseInt(match[1], 10);
	return Number.isFinite(parsed) ? parsed : null;
}
function extractContentHash(zoneText) {
	return zoneText.match(/^\s*;\s*fased-content-hash:\s*(\S+)\s*$/m)?.[1] ?? null;
}
function computeContentHash(body) {
	let h = 2166136261;
	for (let i = 0; i < body.length; i++) {
		h ^= body.charCodeAt(i);
		h = Math.imul(h, 16777619);
	}
	return (h >>> 0).toString(16).padStart(8, "0");
}
function renderZone(opts) {
	const hostname = os.hostname().split(".")[0] ?? "fased";
	const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "fased");
	const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "fased-gw");
	const domain = normalizeWideAreaDomain(opts.domain) ?? "local.";
	const txt = [
		`displayName=${opts.displayName.trim() || hostname}`,
		`role=gateway`,
		`transport=gateway`,
		`gatewayPort=${opts.gatewayPort}`
	];
	if (opts.gatewayTlsEnabled) {
		txt.push(`gatewayTls=1`);
		if (opts.gatewayTlsFingerprintSha256) txt.push(`gatewayTlsSha256=${opts.gatewayTlsFingerprintSha256}`);
	}
	if (opts.tailnetDns?.trim()) txt.push(`tailnetDns=${opts.tailnetDns.trim()}`);
	if (typeof opts.sshPort === "number" && opts.sshPort > 0) txt.push(`sshPort=${opts.sshPort}`);
	if (opts.cliPath?.trim()) txt.push(`cliPath=${opts.cliPath.trim()}`);
	const records = [];
	records.push(`$ORIGIN ${domain}`);
	records.push(`$TTL 60`);
	const soaLine = `@ IN SOA ns1 hostmaster ${opts.serial} 7200 3600 1209600 60`;
	records.push(soaLine);
	records.push(`@ IN NS ns1`);
	records.push(`ns1 IN A ${opts.tailnetIPv4}`);
	records.push(`${hostLabel} IN A ${opts.tailnetIPv4}`);
	if (opts.tailnetIPv6) records.push(`${hostLabel} IN AAAA ${opts.tailnetIPv6}`);
	records.push(`_fased-gw._tcp IN PTR ${instanceLabel}._fased-gw._tcp`);
	records.push(`${instanceLabel}._fased-gw._tcp IN SRV 0 0 ${opts.gatewayPort} ${hostLabel}`);
	records.push(`${instanceLabel}._fased-gw._tcp IN TXT ${txt.map(txtQuote).join(" ")}`);
	const contentBody = `${records.join("\n")}\n`;
	return `; fased-content-hash: ${computeContentHash(`${records.map((line) => line === soaLine ? `@ IN SOA ns1 hostmaster SERIAL 7200 3600 1209600 60` : line).join("\n")}\n`)}\n${contentBody}`;
}
function renderWideAreaGatewayZoneText(opts) {
	return renderZone(opts);
}
async function writeWideAreaGatewayZone(opts) {
	const domain = normalizeWideAreaDomain(opts.domain);
	if (!domain) throw new Error("wide-area discovery domain is required");
	const zonePath = getWideAreaZonePath(domain);
	await ensureDir(path.dirname(zonePath));
	const existing = (() => {
		try {
			return fs.readFileSync(zonePath, "utf-8");
		} catch {
			return null;
		}
	})();
	const nextHash = extractContentHash(renderWideAreaGatewayZoneText({
		...opts,
		serial: 0
	}));
	const existingHash = existing ? extractContentHash(existing) : null;
	if (existing && nextHash && existingHash === nextHash) return {
		zonePath,
		changed: false
	};
	const serial = nextSerial(existing ? extractSerial(existing) : null, /* @__PURE__ */ new Date());
	const next = renderWideAreaGatewayZoneText({
		...opts,
		serial
	});
	fs.writeFileSync(zonePath, next, "utf-8");
	return {
		zonePath,
		changed: true
	};
}

//#endregion
export { resolveWideAreaDiscoveryDomain as n, writeWideAreaGatewayZone as r, getWideAreaZonePath as t };