/**
 * Live Configuration Manager
 *
 * Orchestrates live configuration reload functionality including:
 * - Hook configuration watching and reloading
 * - Configuration file watching for settings.json files
 * - Coordination between file watchers and configuration updates
 */
import { existsSync } from "fs";
import { FileWatcherService, } from "../services/fileWatcher.js";
import { isValidHookEvent } from "../types/hooks.js";
import { ensureGlobalGitIgnore } from "../utils/fileUtils.js";
import { logger } from "../utils/globalLogger.js";
export class LiveConfigManager {
    constructor(container, options) {
        this.container = container;
        this.options = options;
        this.isInitialized = false;
        // Configuration state
        this.currentConfiguration = null;
        this.lastValidConfiguration = null;
        this.isWatching = false;
        this.reloadInProgress = false;
        this.workdir = options.workdir;
        this.fileWatcher = new FileWatcherService(logger);
        this.setupFileWatcherEvents();
    }
    get hookManager() {
        return this.container.get("HookManager");
    }
    get permissionManager() {
        return this.container.get("PermissionManager");
    }
    get configurationService() {
        return this.container.get("ConfigurationService");
    }
    /**
     * Initialize configuration watching
     * Maps to FR-004: System MUST watch settings.json files
     * Supports watching multiple file paths (e.g., local settings.local.json and settings.json)
     */
    async initializeWatching(userPaths, projectPaths) {
        try {
            this.userConfigPaths = userPaths;
            this.projectConfigPaths = projectPaths;
            // Load initial configuration
            await this.reloadConfiguration();
            // Start watching user configs that exist
            for (const userPath of userPaths) {
                if (existsSync(userPath)) {
                    await this.fileWatcher.watchFile(userPath, (event) => this.handleFileChange(event, "user"));
                }
            }
            // Start watching local configs that exist
            if (projectPaths) {
                for (const projectPath of projectPaths) {
                    if (existsSync(projectPath)) {
                        if (projectPath.endsWith("settings.local.json")) {
                            await ensureGlobalGitIgnore("**/.wave/settings.local.json");
                        }
                        await this.fileWatcher.watchFile(projectPath, (event) => this.handleFileChange(event, "project"));
                    }
                }
            }
            this.isWatching = true;
        }
        catch (error) {
            const errorMessage = `Failed to initialize configuration watching: ${error.message}`;
            logger?.error(`Live Config: ${errorMessage}`);
            throw new Error(errorMessage);
        }
    }
    /**
     * Get current configuration
     */
    getCurrentConfiguration() {
        return this.currentConfiguration ? { ...this.currentConfiguration } : null;
    }
    /**
     * Initialize configuration management with file watching
     */
    async initialize() {
        if (this.isInitialized) {
            return;
        }
        try {
            // Get configuration file paths
            const { userPaths, projectPaths } = this.getConfigurationPaths();
            // Initialize configuration watching
            await this.initializeWatching(userPaths, projectPaths);
            this.isInitialized = true;
        }
        catch (error) {
            logger?.error(`Failed to initialize: ${error.message}`);
            throw error;
        }
    }
    /**
     * Shutdown configuration management and cleanup resources
     */
    async shutdown() {
        if (!this.isInitialized) {
            return;
        }
        try {
            this.isWatching = false;
            // Cleanup file watcher
            await this.fileWatcher.cleanup();
            // Clean up state
            this.currentConfiguration = null;
            this.lastValidConfiguration = null;
            this.isInitialized = false;
        }
        catch (error) {
            logger?.error(`Error during shutdown: ${error.message}`);
            throw error;
        }
    }
    /**
     * Reload configuration from files
     * Maps to FR-008: Continue with previous valid configuration on errors
     */
    async reloadConfiguration() {
        if (this.reloadInProgress) {
            return this.currentConfiguration || {};
        }
        this.reloadInProgress = true;
        try {
            // Load merged configuration using ConfigurationService
            const loadResult = await this.configurationService.loadMergedConfiguration(this.workdir);
            const newConfig = loadResult.configuration;
            // Check for errors during loading
            if (!loadResult.success) {
                const errorMessage = loadResult.error || "Configuration loading failed with unknown error";
                logger?.error(`Live Config: Configuration loading failed: ${errorMessage}`);
                // Log warnings if any
                if (loadResult.warnings && loadResult.warnings.length > 0) {
                    logger?.warn(`Live Config: Configuration warnings: ${loadResult.warnings.join("; ")}`);
                }
                // Use fallback configuration if available
                if (this.lastValidConfiguration) {
                    this.currentConfiguration = this.lastValidConfiguration;
                    // Apply environment variables to configuration service if configured
                    if (this.lastValidConfiguration.env) {
                        this.configurationService.setEnvironmentVars(this.lastValidConfiguration.env);
                    }
                    // Update hook manager if available
                    if (this.hookManager) {
                        this.hookManager.loadConfigurationFromWaveConfig(this.lastValidConfiguration);
                    }
                    return this.currentConfiguration;
                }
                else {
                    logger?.warn("Live Config: No previous valid configuration available, using empty config");
                    this.currentConfiguration = {};
                    return this.currentConfiguration;
                }
            }
            // Log warnings from successful loading
            if (loadResult.warnings && loadResult.warnings.length > 0) {
                logger?.warn(`Live Config: Configuration warnings: ${loadResult.warnings.join("; ")}`);
            }
            // Detect changes between old and new configuration
            this.detectChanges(this.currentConfiguration, newConfig);
            // Update current configuration
            this.currentConfiguration = newConfig || {};
            // Save as last valid configuration if it's valid and not empty
            if (newConfig && (newConfig.hooks || newConfig.env)) {
                this.lastValidConfiguration = { ...newConfig };
            }
            // Note: Environment variables are already applied by loadMergedConfiguration()
            // No need to set them again here as currentConfiguration === newConfig
            // Update hook manager if available
            if (this.hookManager) {
                this.hookManager.loadConfigurationFromWaveConfig(this.currentConfiguration);
            }
            // Update permission manager if available
            if (this.permissionManager) {
                this.permissionManager.updateConfiguredPermissionMode(this.currentConfiguration.permissions?.permissionMode);
                this.permissionManager.updateAllowedRules(this.currentConfiguration.permissions?.allow || []);
                this.permissionManager.updateDeniedRules(this.currentConfiguration.permissions?.deny || []);
                this.permissionManager.updateAdditionalDirectories(this.currentConfiguration.permissions?.additionalDirectories || []);
            }
            // Trigger reload callback
            this.options.onReload?.(this.currentConfiguration);
            return this.currentConfiguration;
        }
        catch (error) {
            const errorMessage = `Configuration reload failed with exception: ${error.message}`;
            logger?.error(`Live Config: ${errorMessage}`);
            // Use previous valid configuration for error recovery
            if (this.lastValidConfiguration) {
                this.currentConfiguration = this.lastValidConfiguration;
                // Apply environment variables to configuration service if configured
                if (this.lastValidConfiguration.env) {
                    this.configurationService.setEnvironmentVars(this.lastValidConfiguration.env);
                }
                // Update hook manager if available
                if (this.hookManager) {
                    this.hookManager.loadConfigurationFromWaveConfig(this.lastValidConfiguration);
                }
            }
            else {
                logger?.warn("Live Config: No previous valid configuration available, using empty config");
                this.currentConfiguration = {};
            }
            return this.currentConfiguration;
        }
        finally {
            this.reloadInProgress = false;
        }
    }
    /**
     * Reload configuration from files (public method)
     */
    async reload() {
        return await this.reloadConfiguration();
    }
    /**
     * Check if watching is active
     */
    isWatchingActive() {
        return this.isWatching;
    }
    /**
     * Get watcher status for monitoring
     */
    getWatcherStatus() {
        const statuses = this.fileWatcher.getAllWatcherStatuses();
        return {
            isActive: this.isWatching,
            configurationLoaded: this.currentConfiguration !== null,
            hasValidConfiguration: this.lastValidConfiguration !== null,
            reloadInProgress: this.reloadInProgress,
            watchedFiles: statuses.map((s) => ({
                path: s.path,
                isActive: s.isActive,
                method: s.method,
                errorCount: s.errorCount,
            })),
        };
    }
    setupFileWatcherEvents() {
        this.fileWatcher.on("watcherError", (error) => {
            logger?.error(`Live Config: File watcher error: ${error.message}`);
        });
    }
    async handleFileChange(event, source) {
        try {
            // Handle file deletion
            if (event.type === "delete") {
                // Reload configuration without the deleted file
                await this.reloadConfiguration();
                return;
            }
            // Handle file creation or modification
            if (event.type === "change" || event.type === "create") {
                if (source === "project" &&
                    event.path.endsWith("settings.local.json") &&
                    event.type === "create") {
                    await ensureGlobalGitIgnore("**/.wave/settings.local.json");
                }
                // Add small delay to ensure file write is complete
                await new Promise((resolve) => setTimeout(resolve, 50));
                // Reload configuration
                await this.reloadConfiguration();
            }
        }
        catch (error) {
            logger?.error(`Live Config: Error handling file change for ${source} config: ${error.message}`);
        }
    }
    detectChanges(oldConfig, newConfig) {
        const added = [];
        const modified = [];
        const removed = [];
        // Handle environment variables changes
        const oldEnv = oldConfig?.env || {};
        const newEnv = newConfig?.env || {};
        for (const key of Object.keys(newEnv)) {
            if (!(key in oldEnv)) {
                added.push(`env.${key}`);
            }
            else if (oldEnv[key] !== newEnv[key]) {
                modified.push(`env.${key}`);
            }
        }
        for (const key of Object.keys(oldEnv)) {
            if (!(key in newEnv)) {
                removed.push(`env.${key}`);
            }
        }
        // Handle hooks changes (simplified)
        const oldHooks = oldConfig?.hooks || {};
        const newHooks = newConfig?.hooks || {};
        for (const event of Object.keys(newHooks)) {
            if (isValidHookEvent(event)) {
                if (!(event in oldHooks)) {
                    added.push(`hooks.${event}`);
                }
                else if (JSON.stringify(oldHooks[event]) !== JSON.stringify(newHooks[event])) {
                    modified.push(`hooks.${event}`);
                }
            }
        }
        for (const event of Object.keys(oldHooks)) {
            if (isValidHookEvent(event) && !(event in newHooks)) {
                removed.push(`hooks.${event}`);
            }
        }
        return { added, modified, removed };
    }
    /**
     * Get configuration file paths for user and project settings
     * Returns paths in priority order (local.json first, then .json)
     */
    getConfigurationPaths() {
        const paths = this.configurationService.getConfigurationPaths(this.workdir);
        return {
            userPaths: paths.userPaths,
            projectPaths: paths.projectPaths,
        };
    }
}
