import { baseLogger as logger } from "../utils/logger/index.js";
import { init_logger } from "../utils/logger.js";
import { init_paths, resolveModelsJsonPath } from "./paths.js";
import { init_loader, loadConfig } from "./loader.js";
import { diffConfigPaths } from "./diff.js";
import { BASE_RELOAD_RULES, buildReloadPlan, matchReloadRule } from "./rules.js";
import { watch } from "fs";
//#region src/config/reload.ts
/**
* Configuration hot reload manager
*/
init_loader();
init_paths();
init_logger();
/**
* Configuration hot reload manager
*/
var ConfigHotReloader = class {
	configPath;
	callbacks;
	watcher = null;
	modelsJsonWatcher = null;
	debounceTimer = null;
	currentConfig;
	debounceMs;
	enabled;
	constructor(configPath, initialConfig, callbacks, options = {
		debounceMs: 300,
		enabled: true
	}) {
		this.configPath = configPath;
		this.currentConfig = initialConfig;
		this.callbacks = callbacks;
		this.debounceMs = options.debounceMs;
		this.enabled = options.enabled;
	}
	/**
	* Start watching config file for changes
	*/
	start() {
		if (!this.enabled) {
			logger.info("Config hot reload disabled");
			return;
		}
		try {
			this.watcher = watch(this.configPath, (eventType) => {
				if (eventType === "change") this.scheduleReload();
			});
			logger.info({ path: this.configPath }, "Config hot reload enabled");
		} catch (err) {
			logger.error({ err }, "Failed to setup config watcher");
		}
		this.startModelsJsonWatcher();
	}
	/**
	* Stop watching config file
	*/
	async stop() {
		if (this.watcher) {
			this.watcher.close();
			this.watcher = null;
		}
		if (this.modelsJsonWatcher) {
			this.modelsJsonWatcher.close();
			this.modelsJsonWatcher = null;
		}
		if (this.debounceTimer) {
			clearTimeout(this.debounceTimer);
			this.debounceTimer = null;
		}
		logger.info("Config hot reload stopped");
	}
	/**
	* Schedule a reload with debounce
	*/
	scheduleReload() {
		if (this.debounceTimer) clearTimeout(this.debounceTimer);
		this.debounceTimer = setTimeout(() => {
			this.reload();
		}, this.debounceMs);
	}
	/**
	* Watch models.json independently so direct file edits are picked up without restarting.
	* The watcher is best-effort: if the file does not exist yet, it will not be watched.
	*/
	startModelsJsonWatcher() {
		const modelsJsonPath = resolveModelsJsonPath();
		try {
			this.modelsJsonWatcher = watch(modelsJsonPath, (eventType) => {
				if (eventType === "change") this.scheduleModelsJsonReload();
			});
			logger.info({ path: modelsJsonPath }, "models.json hot reload enabled");
		} catch {
			logger.debug({ path: modelsJsonPath }, "models.json not found, skipping watcher");
		}
	}
	/**
	* Debounced handler for models.json changes.
	* Calls onModelsReload with the current config so the registry is refreshed.
	*/
	scheduleModelsJsonReload() {
		if (this.debounceTimer) clearTimeout(this.debounceTimer);
		this.debounceTimer = setTimeout(() => {
			logger.info("models.json changed on disk — refreshing ModelRegistry");
			if (this.callbacks.onModelsReload) Promise.resolve(this.callbacks.onModelsReload(this.currentConfig));
		}, this.debounceMs);
	}
	/**
	* Reload configuration and apply changes
	*/
	async reload() {
		try {
			logger.info("Reloading configuration...");
			const newConfig = loadConfig(this.configPath);
			const changedPaths = diffConfigPaths(this.currentConfig, newConfig);
			if (changedPaths.length === 0) {
				logger.debug("No config changes detected");
				return { success: true };
			}
			logger.info({ changedPaths }, "Config changes detected");
			const plan = buildReloadPlan(changedPaths);
			await this.applyReload(plan, newConfig);
			this.currentConfig = newConfig;
			return {
				success: true,
				plan
			};
		} catch (err) {
			const error = err instanceof Error ? err.message : String(err);
			logger.error({
				err,
				configPath: this.configPath,
				errorMessage: error
			}, `Config hot reload failed: ${error}`);
			return {
				success: false,
				error
			};
		}
	}
	/**
	* Apply reload based on plan
	*/
	async applyReload(plan, newConfig) {
		if (plan.requiresRestart) {
			logger.info({ restartPaths: plan.restartPaths }, "Config changes require gateway restart");
			if (this.callbacks.onFullRestart) this.callbacks.onFullRestart(newConfig);
			return;
		}
		if (!plan.requiresHotReload) {
			logger.info({ plan }, "Config hot reload completed");
			return;
		}
		const isExtensionPath = (p) => p === "extensions" || p.startsWith("extensions.");
		const extensionPaths = plan.hotPaths.filter(isExtensionPath);
		const otherPaths = plan.hotPaths.filter((p) => !isExtensionPath(p));
		for (const path of otherPaths) await this.applyHotPath(path, newConfig);
		if (extensionPaths.length > 0 && this.callbacks.onExtensionsReload) await Promise.resolve(this.callbacks.onExtensionsReload(newConfig, extensionPaths));
		logger.info({ plan }, "Config hot reload completed");
	}
	/**
	* Apply a single hot-reloadable path
	*/
	async applyHotPath(path, newConfig) {
		if (path.startsWith("models.")) {
			if (this.callbacks.onModelsReload) await Promise.resolve(this.callbacks.onModelsReload(newConfig));
			return;
		}
		if (path.startsWith("agents.defaults.")) {
			if (this.callbacks.onAgentDefaultsReload) await Promise.resolve(this.callbacks.onAgentDefaultsReload(newConfig));
			return;
		}
		if (path.startsWith("channels.")) {
			if (this.callbacks.onChannelsReload) await Promise.resolve(this.callbacks.onChannelsReload(newConfig));
			return;
		}
		if (path.startsWith("cron.")) {
			if (this.callbacks.onCronReload) await Promise.resolve(this.callbacks.onCronReload(newConfig));
			return;
		}
		if (path === "gateway.heartbeat" || path.startsWith("gateway.heartbeat.")) {
			if (this.callbacks.onHeartbeatReload) await Promise.resolve(this.callbacks.onHeartbeatReload(newConfig));
			return;
		}
		if (path.startsWith("tools.")) {
			if (this.callbacks.onToolsReload) await Promise.resolve(this.callbacks.onToolsReload(newConfig));
			return;
		}
		if (path.startsWith("mcp.") || path === "mcp") {
			if (this.callbacks.onMcpReload) await Promise.resolve(this.callbacks.onMcpReload(newConfig));
			return;
		}
		if (path.startsWith("webSearch.") || path.startsWith("webTools.")) {
			if (this.callbacks.onWebSearchReload) await Promise.resolve(this.callbacks.onWebSearchReload(newConfig));
			return;
		}
		logger.debug({ path }, "No handler for hot reload path");
	}
	/**
	* Manually trigger a reload
	*/
	async triggerReload() {
		return this.reload();
	}
	/**
	* Align diff baseline with a freshly loaded config (e.g. Weixin QR wrote token files but JSON unchanged).
	*/
	syncCurrentConfig(config) {
		this.currentConfig = config;
	}
	/**
	* Get current config
	*/
	getConfig() {
		return this.currentConfig;
	}
	/**
	* Check if hot reload is enabled
	*/
	isEnabled() {
		return this.enabled;
	}
};
//#endregion
export { BASE_RELOAD_RULES, ConfigHotReloader, buildReloadPlan, diffConfigPaths, matchReloadRule };

//# sourceMappingURL=reload.js.map