import { isValidIPv4 } from "../../../../config/gateway-bind.js";
import { mergeShareConfigPatch } from "../../../../share/share-config.js";
import { isMaskedSecretPatchValue } from "../../lib/mask-secret-length.js";
import { PATCH_OK, patchError } from "./result.js";
//#region src/gateway/hono/routes/config-patch/gateway.ts
/**
* Shared default for "user PATCH'd a gateway subsection but `config.gateway`
* was never initialised". Identical to the inline literal repeated in the
* pre-extraction handler so this is behavior-preserving.
*/
function ensureGateway(config) {
	if (!config.gateway) config.gateway = {
		bind: "loopback",
		port: 18790,
		heartbeat: {
			enabled: true,
			intervalMs: 18e5,
			includeSystemPromptSection: false
		},
		maxSseConnections: 100,
		corsOrigins: []
	};
	return config.gateway;
}
function parseDeferIdList(raw) {
	if (!Array.isArray(raw)) return null;
	const ids = raw.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
	if (ids.length > 24) return null;
	return ids;
}
function applyGatewayPatch(config, body) {
	if (body.gateway?.heartbeat !== void 0 && typeof body.gateway.heartbeat === "object") {
		const gw = ensureGateway(config);
		if (!gw.heartbeat) gw.heartbeat = {
			enabled: true,
			intervalMs: 18e5,
			includeSystemPromptSection: false
		};
		const h = gw.heartbeat;
		const p = body.gateway.heartbeat;
		if (p.enabled !== void 0) h.enabled = Boolean(p.enabled);
		if (p.intervalMs !== void 0 && typeof p.intervalMs === "number" && Number.isFinite(p.intervalMs)) h.intervalMs = p.intervalMs;
		if (p.includeSystemPromptSection !== void 0) h.includeSystemPromptSection = Boolean(p.includeSystemPromptSection);
		if (p.target !== void 0) if (p.target === null || p.target === "") delete h.target;
		else h.target = String(p.target);
		if (p.targetChatId !== void 0) if (p.targetChatId === null || p.targetChatId === "") delete h.targetChatId;
		else h.targetChatId = String(p.targetChatId);
		if (p.prompt !== void 0) if (p.prompt === null || p.prompt === "") delete h.prompt;
		else h.prompt = String(p.prompt);
		if (p.ackMaxChars !== void 0) {
			if (p.ackMaxChars === null || p.ackMaxChars === "") delete h.ackMaxChars;
			else if (typeof p.ackMaxChars === "number" && Number.isFinite(p.ackMaxChars)) h.ackMaxChars = p.ackMaxChars;
		}
		if (p.isolatedSession !== void 0) if (p.isolatedSession === null || p.isolatedSession === false) delete h.isolatedSession;
		else h.isolatedSession = Boolean(p.isolatedSession);
		if (p.activeHours !== void 0) {
			if (p.activeHours === null) delete h.activeHours;
			else if (typeof p.activeHours === "object" && p.activeHours !== null) {
				const ah = p.activeHours;
				const start = typeof ah.start === "string" ? ah.start : "";
				const end = typeof ah.end === "string" ? ah.end : "";
				if (start && end) h.activeHours = {
					start,
					end,
					...typeof ah.timezone === "string" && ah.timezone.trim() ? { timezone: ah.timezone } : {}
				};
				else delete h.activeHours;
			}
		}
	}
	if (body.gateway?.bind !== void 0) {
		const bindModes = new Set([
			"auto",
			"loopback",
			"lan",
			"tailnet",
			"custom"
		]);
		const bind = body.gateway.bind;
		if (typeof bind !== "string" || !bindModes.has(bind)) return patchError("gateway.bind must be one of: auto, loopback, lan, tailnet, custom");
		if (!config.gateway) config.gateway = {
			bind,
			port: 18790,
			heartbeat: {
				enabled: true,
				intervalMs: 18e5,
				includeSystemPromptSection: false
			},
			maxSseConnections: 100,
			corsOrigins: []
		};
		else config.gateway.bind = bind;
		if (bind !== "custom") delete config.gateway.customBindHost;
	}
	if (body.gateway?.customBindHost !== void 0) {
		if (body.gateway.customBindHost === null || body.gateway.customBindHost === "") {
			if (config.gateway) delete config.gateway.customBindHost;
		} else if (typeof body.gateway.customBindHost !== "string" || !isValidIPv4(body.gateway.customBindHost.trim())) return patchError("gateway.customBindHost must be a valid IPv4 address");
		else if (config.gateway) {
			config.gateway.customBindHost = body.gateway.customBindHost.trim();
			config.gateway.bind = "custom";
		}
	}
	if (body.gateway?.port !== void 0) {
		if (typeof body.gateway.port !== "number" || !Number.isFinite(body.gateway.port) || body.gateway.port < 1 || body.gateway.port > 65535) return patchError("gateway.port must be an integer from 1 to 65535");
		if (!config.gateway) config.gateway = {
			bind: "loopback",
			port: Math.floor(body.gateway.port),
			heartbeat: {
				enabled: true,
				intervalMs: 18e5,
				includeSystemPromptSection: false
			},
			maxSseConnections: 100,
			corsOrigins: []
		};
		else config.gateway.port = Math.floor(body.gateway.port);
	}
	if (body.gateway?.tailscale !== void 0 && typeof body.gateway.tailscale === "object") {
		const ts = body.gateway.tailscale;
		if (!config.gateway) config.gateway = {
			bind: "loopback",
			port: 18790,
			auth: { mode: "token" },
			heartbeat: {
				enabled: true,
				intervalMs: 18e5,
				includeSystemPromptSection: false
			},
			maxSseConnections: 100,
			corsOrigins: []
		};
		config.gateway.tailscale = { ...config.gateway.tailscale ?? {
			mode: "off",
			resetOnExit: true
		} };
		if (ts.mode !== void 0) {
			if (ts.mode !== "off" && ts.mode !== "serve" && ts.mode !== "funnel") return patchError("gateway.tailscale.mode must be off, serve, or funnel");
			config.gateway.tailscale.mode = ts.mode;
		}
		if (ts.resetOnExit !== void 0) config.gateway.tailscale.resetOnExit = ts.resetOnExit === true;
	}
	if (body.gateway?.auth !== void 0) {
		const gw = ensureGateway(config);
		if (!gw.auth) gw.auth = { mode: "token" };
		const a = body.gateway.auth;
		if (a.mode !== void 0) {
			if (a.mode !== "none" && a.mode !== "token" && a.mode !== "password" && a.mode !== "trusted-proxy") return patchError("gateway.auth.mode must be none, token, password, or trusted-proxy");
			gw.auth.mode = a.mode;
		}
		if (a.token !== void 0) {
			if (a.token === null || typeof a.token === "string" && !a.token.trim()) delete gw.auth.token;
			else if (typeof a.token === "string" && !isMaskedSecretPatchValue(a.token)) gw.auth.token = a.token;
		}
		if (a.password !== void 0) {
			if (a.password === null || typeof a.password === "string" && !a.password.trim()) delete gw.auth.password;
			else if (typeof a.password === "string" && !isMaskedSecretPatchValue(a.password)) gw.auth.password = a.password;
		}
		if (a.rateLimit !== void 0 && typeof a.rateLimit === "object" && a.rateLimit !== null) {
			const rlIn = a.rateLimit;
			if (!gw.auth.rateLimit) gw.auth.rateLimit = {
				enabled: true,
				maxAttempts: 5,
				windowMs: 9e5,
				blockDurationMs: 3e5,
				burstCoalesceMs: 1e3,
				exemptLoopback: true
			};
			const rl = gw.auth.rateLimit;
			if (rlIn.enabled !== void 0) rl.enabled = Boolean(rlIn.enabled);
			if (typeof rlIn.maxAttempts === "number" && Number.isFinite(rlIn.maxAttempts)) rl.maxAttempts = Math.max(1, Math.floor(rlIn.maxAttempts));
			if (typeof rlIn.windowMs === "number" && Number.isFinite(rlIn.windowMs) && rlIn.windowMs > 0) rl.windowMs = Math.floor(rlIn.windowMs);
			if (typeof rlIn.blockDurationMs === "number" && Number.isFinite(rlIn.blockDurationMs) && rlIn.blockDurationMs > 0) rl.blockDurationMs = Math.floor(rlIn.blockDurationMs);
			if (typeof rlIn.lockoutMs === "number" && Number.isFinite(rlIn.lockoutMs) && rlIn.lockoutMs > 0) rl.blockDurationMs = Math.floor(rlIn.lockoutMs);
			if (rlIn.exemptLoopback !== void 0) rl.exemptLoopback = Boolean(rlIn.exemptLoopback);
		}
		if (a.trustedProxy !== void 0) {
			if (a.trustedProxy === null) delete gw.auth.trustedProxy;
			else if (typeof a.trustedProxy === "object" && a.trustedProxy !== null) {
				const tpIn = a.trustedProxy;
				const userHeader = typeof tpIn.userHeader === "string" ? tpIn.userHeader.trim() : "";
				if (!userHeader) return patchError("gateway.auth.trustedProxy.userHeader is required");
				const trustedProxy = { userHeader };
				if (tpIn.requiredHeaders !== void 0) {
					if (!Array.isArray(tpIn.requiredHeaders)) return patchError("gateway.auth.trustedProxy.requiredHeaders must be an array");
					trustedProxy.requiredHeaders = tpIn.requiredHeaders.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
				}
				if (tpIn.allowUsers !== void 0) {
					if (!Array.isArray(tpIn.allowUsers)) return patchError("gateway.auth.trustedProxy.allowUsers must be an array");
					trustedProxy.allowUsers = tpIn.allowUsers.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
				}
				if (tpIn.allowLoopback !== void 0) trustedProxy.allowLoopback = Boolean(tpIn.allowLoopback);
				gw.auth.trustedProxy = trustedProxy;
			}
		}
	}
	if (body.gateway?.trustedProxies !== void 0) {
		if (!Array.isArray(body.gateway.trustedProxies)) return patchError("gateway.trustedProxies must be an array");
		const gw = ensureGateway(config);
		gw.trustedProxies = body.gateway.trustedProxies.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
	}
	if (body.gateway?.allowRealIpFallback !== void 0) {
		const gw = ensureGateway(config);
		gw.allowRealIpFallback = Boolean(body.gateway.allowRealIpFallback);
	}
	if (body.gateway?.dangerouslyAllowHostHeaderOriginFallback !== void 0) {
		const gw = ensureGateway(config);
		gw.dangerouslyAllowHostHeaderOriginFallback = Boolean(body.gateway.dangerouslyAllowHostHeaderOriginFallback);
	}
	if (body.gateway?.security !== void 0) {
		if (typeof body.gateway.security !== "object" || body.gateway.security === null) return patchError("gateway.security must be an object");
		const gw = ensureGateway(config);
		const secIn = body.gateway.security;
		if (!gw.security) gw.security = {};
		if (secIn.strict !== void 0) gw.security.strict = Boolean(secIn.strict);
	}
	if (body.gateway?.share !== void 0) {
		if (typeof body.gateway.share !== "object" || body.gateway.share === null || Array.isArray(body.gateway.share)) return patchError("gateway.share must be an object");
		const shareResult = mergeShareConfigPatch(config, body.gateway.share);
		if (shareResult.ok === false) return patchError(shareResult.message);
	}
	if (body.gateway?.corsOrigins !== void 0) {
		if (!Array.isArray(body.gateway.corsOrigins)) return patchError("gateway.corsOrigins must be an array");
		const gw = ensureGateway(config);
		gw.corsOrigins = body.gateway.corsOrigins.filter((x) => typeof x === "string" && x.trim().length > 0).map((x) => x.trim());
	}
	if (body.gateway?.maxSseConnections !== void 0) {
		if (typeof body.gateway.maxSseConnections !== "number" || !Number.isFinite(body.gateway.maxSseConnections) || body.gateway.maxSseConnections < 1) return patchError("gateway.maxSseConnections must be a positive integer");
		if (!config.gateway) config.gateway = {
			bind: "loopback",
			port: 18790,
			heartbeat: {
				enabled: true,
				intervalMs: 18e5,
				includeSystemPromptSection: false
			},
			maxSseConnections: Math.floor(body.gateway.maxSseConnections),
			corsOrigins: []
		};
		else config.gateway.maxSseConnections = Math.floor(body.gateway.maxSseConnections);
	}
	if (body.gateway?.channelConnectDeferMode !== void 0) {
		const mode = body.gateway.channelConnectDeferMode;
		if (mode !== "auto" && mode !== "off" && mode !== "explicit") return patchError("gateway.channelConnectDeferMode must be auto, off, or explicit");
		const gw = ensureGateway(config);
		gw.channelConnectDeferMode = mode;
	}
	if (body.gateway?.channelConnectDeferIds !== void 0) {
		const ids = parseDeferIdList(body.gateway.channelConnectDeferIds);
		if (ids === null) return patchError("gateway.channelConnectDeferIds must be an array of up to 24 strings");
		const gw = ensureGateway(config);
		gw.channelConnectDeferIds = ids;
	}
	if (body.gateway?.channelConnectDeferSkipIds !== void 0) {
		const ids = parseDeferIdList(body.gateway.channelConnectDeferSkipIds);
		if (ids === null) return patchError("gateway.channelConnectDeferSkipIds must be an array of up to 24 strings");
		const gw = ensureGateway(config);
		gw.channelConnectDeferSkipIds = ids;
	}
	return PATCH_OK;
}
//#endregion
export { applyGatewayPatch };

//# sourceMappingURL=gateway.js.map