/**
 * config-patch handler — PR-5a
 *
 * JSON deep-merge into a target config file (e.g. .claude/settings.json).
 * Idempotent: duplicate hook entries are detected and skipped.
 *
 * Merge semantics:
 *   - Plain objects: keys from patch are merged into target (deep).
 *   - Arrays: patch array items appended unless already present (by value equality).
 *   - Primitive values: patch wins over existing.
 *   - If the target file does not exist, the patch becomes the initial content.
 *
 * @connexum/ai-governance PR-5a
 */
import * as fs from 'fs';
import * as path from 'path';
import { computeUnifiedDiff } from '../diff-utils.js';
/**
 * Compute the patched content for a config file.
 * Returns the before + after strings so the caller can decide to write or not.
 */
export function applyConfigPatch(agentDir, payload) {
    const filePath = path.resolve(agentDir, payload.targetFile);
    const before = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';
    let existing = {};
    if (before.trim()) {
        try {
            existing = JSON.parse(before);
        }
        catch {
            throw new Error(`config-patch: cannot parse existing file as JSON: ${filePath}`);
        }
    }
    const merged = deepMerge(existing, payload.patch);
    const after = JSON.stringify(merged, null, 2) + '\n';
    const alreadyApplied = after === (before.trim() ? JSON.stringify(JSON.parse(before), null, 2) + '\n' : '');
    const diff = {
        filePath: path.relative(agentDir, filePath),
        before,
        after,
        unifiedDiff: computeUnifiedDiff(before, after, path.relative(agentDir, filePath)),
    };
    return { filePath, before, after, diff, alreadyApplied };
}
/**
 * Write the patched config file to disk.
 */
export function writeConfigPatch(agentDir, payload) {
    const result = applyConfigPatch(agentDir, payload);
    if (!result.alreadyApplied) {
        fs.mkdirSync(path.dirname(result.filePath), { recursive: true });
        fs.writeFileSync(result.filePath, result.after, 'utf-8');
    }
    return result;
}
// ---------------------------------------------------------------------------
// Deep merge
// ---------------------------------------------------------------------------
function deepMerge(target, patch) {
    const result = { ...target };
    for (const [key, patchVal] of Object.entries(patch)) {
        const targetVal = target[key];
        if (patchVal !== null &&
            typeof patchVal === 'object' &&
            !Array.isArray(patchVal) &&
            targetVal !== null &&
            typeof targetVal === 'object' &&
            !Array.isArray(targetVal)) {
            result[key] = deepMerge(targetVal, patchVal);
        }
        else if (Array.isArray(patchVal) && Array.isArray(targetVal)) {
            // Idempotent array merge: append only items not already present
            const merged = [...targetVal];
            for (const item of patchVal) {
                const alreadyPresent = merged.some((existing) => JSON.stringify(existing) === JSON.stringify(item));
                if (!alreadyPresent) {
                    merged.push(item);
                }
            }
            result[key] = merged;
        }
        else {
            result[key] = patchVal;
        }
    }
    return result;
}
//# sourceMappingURL=config-patch.js.map