"use strict";
/**
 * Diff Engine
 * Generates diffs between two configuration states
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DiffEngine = void 0;
class DiffEngine {
    /**
     * Generate diff between two configurations
     */
    generateDiff(oldConfig, newConfig, options = {}) {
        const changes = [];
        // Compare objects recursively
        this.compareObjects(oldConfig || {}, newConfig || {}, '', changes, options);
        return changes;
    }
    /**
     * Recursively compare two objects
     */
    compareObjects(oldObj, newObj, path, changes, options) {
        // Get all unique keys from both objects
        const allKeys = new Set([
            ...Object.keys(oldObj || {}),
            ...Object.keys(newObj || {}),
        ]);
        for (const key of allKeys) {
            const currentPath = path ? `${path}/${key}` : `/${key}`;
            const oldValue = oldObj?.[key];
            const newValue = newObj?.[key];
            // Skip if should ignore
            if (this.shouldIgnore(key, options)) {
                continue;
            }
            // Key exists in new but not old (added)
            if (oldValue === undefined && newValue !== undefined) {
                changes.push({
                    op: 'add',
                    path: currentPath,
                    value: newValue,
                });
                continue;
            }
            // Key exists in old but not new (removed)
            if (oldValue !== undefined && newValue === undefined) {
                changes.push({
                    op: 'remove',
                    path: currentPath,
                    oldValue: oldValue,
                });
                continue;
            }
            // Both exist - check if modified
            if (this.isObject(oldValue) && this.isObject(newValue)) {
                // Recursively compare objects
                this.compareObjects(oldValue, newValue, currentPath, changes, options);
            }
            else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
                // Compare arrays
                this.compareArrays(oldValue, newValue, currentPath, changes, options);
            }
            else if (!this.areEqual(oldValue, newValue)) {
                // Values are different
                changes.push({
                    op: 'modify',
                    path: currentPath,
                    oldValue: oldValue,
                    value: newValue,
                });
            }
        }
    }
    /**
     * Compare two arrays
     */
    compareArrays(oldArray, newArray, path, changes, options) {
        // For arrays, we use index-based comparison
        const maxLength = Math.max(oldArray.length, newArray.length);
        for (let i = 0; i < maxLength; i++) {
            const currentPath = `${path}/${i}`;
            const oldValue = oldArray[i];
            const newValue = newArray[i];
            if (oldValue === undefined && newValue !== undefined) {
                // Item added
                changes.push({
                    op: 'add',
                    path: currentPath,
                    value: newValue,
                });
            }
            else if (oldValue !== undefined && newValue === undefined) {
                // Item removed
                changes.push({
                    op: 'remove',
                    path: currentPath,
                    oldValue: oldValue,
                });
            }
            else if (this.isObject(oldValue) && this.isObject(newValue)) {
                // Compare object items
                this.compareObjects(oldValue, newValue, currentPath, changes, options);
            }
            else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
                // Nested array
                this.compareArrays(oldValue, newValue, currentPath, changes, options);
            }
            else if (!this.areEqual(oldValue, newValue)) {
                // Item modified
                changes.push({
                    op: 'modify',
                    path: currentPath,
                    oldValue: oldValue,
                    value: newValue,
                });
            }
        }
    }
    /**
     * Check if two values are equal
     */
    areEqual(a, b) {
        // Handle primitives
        if (a === b)
            return true;
        // Handle null/undefined
        if (a === null || b === null)
            return a === b;
        if (a === undefined || b === undefined)
            return a === b;
        // Handle dates
        if (a instanceof Date && b instanceof Date) {
            return a.getTime() === b.getTime();
        }
        // For objects/arrays, do deep comparison
        if (typeof a === 'object' && typeof b === 'object') {
            return JSON.stringify(a) === JSON.stringify(b);
        }
        return false;
    }
    /**
     * Check if value is a plain object
     */
    isObject(value) {
        return value !== null && typeof value === 'object' && !Array.isArray(value);
    }
    /**
     * Check if key should be ignored
     */
    shouldIgnore(key, options) {
        if (options.ignoreMetadata && key === 'metadata') {
            return true;
        }
        if (options.ignoreComments && (key === 'comments' || key === 'description')) {
            return true;
        }
        return false;
    }
    /**
     * Apply a diff to a configuration
     */
    applyDiff(config, diff) {
        const result = JSON.parse(JSON.stringify(config)); // Deep clone
        for (const change of diff) {
            this.applyChange(result, change);
        }
        return result;
    }
    /**
     * Apply a single change
     */
    applyChange(config, change) {
        const pathParts = change.path.split('/').filter(Boolean);
        if (pathParts.length === 0)
            return;
        // Navigate to parent
        let current = config;
        for (let i = 0; i < pathParts.length - 1; i++) {
            const part = pathParts[i];
            if (current[part] === undefined) {
                // Create intermediate objects/arrays as needed
                const nextPart = pathParts[i + 1];
                current[part] = /^\d+$/.test(nextPart) ? [] : {};
            }
            current = current[part];
        }
        const lastKey = pathParts[pathParts.length - 1];
        switch (change.op) {
            case 'add':
            case 'modify':
                current[lastKey] = change.value;
                break;
            case 'remove':
                if (Array.isArray(current)) {
                    current.splice(parseInt(lastKey), 1);
                }
                else {
                    delete current[lastKey];
                }
                break;
        }
    }
    /**
     * Get summary of changes
     */
    getSummary(diff) {
        return {
            added: diff.filter(c => c.op === 'add').length,
            removed: diff.filter(c => c.op === 'remove').length,
            modified: diff.filter(c => c.op === 'modify').length,
        };
    }
    /**
     * Determine which files would be affected by changes
     */
    getAffectedFiles(diff) {
        const files = new Set();
        for (const change of diff) {
            const pathParts = change.path.split('/').filter(Boolean);
            const [section] = pathParts;
            switch (section) {
                case 'actions':
                    // Action changes affect implementations and docs
                    if (change.op === 'add' && change.value?.id) {
                        const actionId = change.value.id;
                        files.add(`src/implementations/*/${actionId}.{py,js,ts,go}`);
                        files.add(`docs/actions/${actionId}.md`);
                    }
                    break;
                case 'implementations':
                    // Implementation changes
                    if (change.value?.location?.file) {
                        files.add(change.value.location.file);
                    }
                    break;
                case 'workflows':
                    // Workflow changes affect docs
                    if (change.value?.id) {
                        files.add(`docs/workflows/${change.value.id}.md`);
                    }
                    break;
                case 'events':
                    // Event changes might affect event router
                    files.add('src/events/router.{py,js,ts}');
                    break;
            }
        }
        return Array.from(files);
    }
}
exports.DiffEngine = DiffEngine;
//# sourceMappingURL=diff-engine.js.map