/**
 * Configuration Manager
 * Handles reading, writing, and managing configuration files
 */
import { ConfigPaths, DEFAULT_CONFIG, getConfigValue, setConfigValue, } from '../config/ConfigSchema.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { fsPromises as fs } from '../../node-singletons/fs.js';
import * as path from '../../node-singletons/path.js';
/**
 * Deep merge helper
 */
const deepMerge = (target, source) => {
    const result = { ...target };
    for (const key in source) {
        if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
            result[key] = deepMerge(target[key] ?? {}, source[key]);
        }
        else {
            result[key] = source[key];
        }
    }
    return result;
};
/**
 * Load configuration from file
 */
async function loadConfig(state) {
    try {
        const content = await fs.readFile(state.configPath, 'utf-8');
        state.config = JSON.parse(content);
        return state.config;
    }
    catch (err) {
        if (err.code === 'ENOENT') {
            Logger.debug(`Config file not found at ${state.configPath}, using defaults`);
            state.config = structuredClone(DEFAULT_CONFIG);
            return state.config;
        }
        ErrorFactory.createCliError(`Failed to load config: ${err.message}`);
        throw err;
    }
}
/**
 * Save configuration to file
 */
async function saveConfig(state, newConfig) {
    const toSave = newConfig ?? state.config;
    if (!toSave) {
        throw ErrorFactory.createConfigError('No configuration to save');
    }
    try {
        // Ensure directory exists
        const dir = path.dirname(state.configPath);
        if (dir !== '.') {
            await fs.mkdir(dir, { recursive: true });
        }
        // Write config with nice formatting
        await fs.writeFile(state.configPath, JSON.stringify(toSave, null, 2));
        state.config = toSave;
        Logger.debug(`Config saved to ${state.configPath}`);
    }
    catch (err) {
        ErrorFactory.createCliError(`Failed to save config: ${err.message}`);
        throw err;
    }
}
/**
 * Get current configuration
 */
function getConfig(state) {
    state.config ??= structuredClone(DEFAULT_CONFIG);
    return state.config;
}
/**
 * Check if config file exists
 */
async function configExists(configPath) {
    try {
        await fs.access(configPath);
        return true;
    }
    catch (error) {
        ErrorFactory.createCliError('Config file access check failed', error);
        return false;
    }
}
/**
 * Flatten object keys
 */
function flattenKeys(obj, prefix = '') {
    const keys = [];
    for (const key in obj) {
        const fullKey = prefix ? `${prefix}.${key}` : key;
        const value = obj[key];
        if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
            keys.push(...flattenKeys(value, fullKey));
        }
        else {
            keys.push(fullKey);
        }
    }
    return keys;
}
/**
 * Configuration Manager
 * Handles reading, writing, and managing configuration files
 * Sealed namespace for immutability
 */
export const ConfigManager = Object.freeze({
    /**
     * Create a new config manager instance
     */
    create(configPath = ConfigPaths.PROJECT_CONFIG) {
        const state = {
            config: null,
            configPath,
        };
        return {
            load: async () => loadConfig(state),
            save: async (newConfig) => saveConfig(state, newConfig),
            getConfig: () => getConfig(state),
            get(key) {
                return getConfigValue(getConfig(state), key);
            },
            set(key, value) {
                setConfigValue(getConfig(state), key, value);
            },
            exists: async () => configExists(configPath),
            async create(initialConfig) {
                const newConfig = { ...DEFAULT_CONFIG, ...initialConfig };
                await saveConfig(state, newConfig);
            },
            async reset() {
                state.config = structuredClone(DEFAULT_CONFIG);
                await saveConfig(state);
            },
            merge(partial) {
                const currentConfig = getConfig(state);
                state.config = deepMerge(currentConfig, partial);
            },
            export: () => JSON.stringify(getConfig(state), null, 2),
            getAllKeys: () => flattenKeys(getConfig(state)),
        };
    },
    /**
     * Create global config directory if not exists
     */
    async ensureGlobalConfigDir() {
        try {
            await fs.mkdir(ConfigPaths.GLOBAL_DIR, { recursive: true });
        }
        catch (err) {
            ErrorFactory.createCliError('Could not create global config dir', err);
            Logger.debug(`Could not create global config dir: ${err.message}`);
        }
    },
    /**
     * Get or create global config manager
     */
    async getGlobalConfig() {
        await this.ensureGlobalConfigDir();
        const manager = this.create(ConfigPaths.GLOBAL_CONFIG);
        await manager.load();
        return manager;
    },
    /**
     * Get or create project config manager
     */
    async getProjectConfig() {
        const manager = this.create(ConfigPaths.PROJECT_CONFIG);
        await manager.load();
        return manager;
    },
});
