/**
 * Sync Bindings for Unified Scripting
 *
 * Provides scripting functions for cross-session synchronization:
 * - sync.status() - Get current sync status
 * - sync.push() - Push local changes
 * - sync.pull() - Pull remote changes
 * - sync.diff() - Show pending changes
 * - sync.resolve() - Resolve conflicts
 * - sync.snapshot() - Create workspace snapshot
 * - sync.restore() - Restore from snapshot
 *
 * Migrated from src/fcs/sync-bindings.ts
 */
import { getSyncManager, } from '../sync/index.js';
// ============================================================================
// Workspace State Tracker
// ============================================================================
export class WorkspaceStateTracker {
    files = new Map();
    sessionContext;
    syncManager;
    snapshots = new Map();
    constructor(sessionId, nodeId) {
        this.sessionContext = {
            sessionId,
            agentMode: 'code',
            toolsUsed: [],
            conversationLength: 0,
            startTime: Date.now(),
        };
        this.syncManager = getSyncManager({
            nodeId: nodeId || `session_${sessionId}`,
            conflictStrategy: 'merge',
            autoSync: false,
        });
    }
    async initialize() {
        const states = this.syncManager.getAllStates();
        if (states.length > 0) {
            states.sort((a, b) => b.timestamp - a.timestamp);
            const latest = states[0];
            if (latest.data) {
                this.restoreSnapshotFromData(latest.data);
            }
        }
    }
    restoreSnapshotFromData(snapshot) {
        this.files = new Map(snapshot.files);
        this.sessionContext = { ...snapshot.context };
        this.snapshots.set(snapshot.id, snapshot);
    }
    trackFile(filePath, content, isOpen = false) {
        const hash = this.computeHash(content);
        const existing = this.files.get(filePath);
        this.files.set(filePath, {
            path: filePath,
            content,
            hash,
            lastModified: Date.now(),
            isOpen,
            isDirty: existing ? existing.hash !== hash : false,
        });
    }
    untrackFile(filePath) {
        this.files.delete(filePath);
    }
    markFileDirty(filePath) {
        const file = this.files.get(filePath);
        if (file) {
            file.isDirty = true;
            file.lastModified = Date.now();
        }
    }
    markFileClean(filePath) {
        const file = this.files.get(filePath);
        if (file) {
            file.isDirty = false;
        }
    }
    getTrackedFiles() {
        return Array.from(this.files.values());
    }
    getDirtyFiles() {
        return this.getTrackedFiles().filter(f => f.isDirty);
    }
    updateContext(updates) {
        this.sessionContext = { ...this.sessionContext, ...updates };
    }
    recordToolUsage(toolName) {
        if (!this.sessionContext.toolsUsed.includes(toolName)) {
            this.sessionContext.toolsUsed.push(toolName);
        }
    }
    incrementConversation() {
        this.sessionContext.conversationLength++;
    }
    getContext() {
        return { ...this.sessionContext };
    }
    createSnapshot(metadata = {}) {
        const snapshot = {
            id: `snap_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
            timestamp: Date.now(),
            files: new Map(this.files),
            context: { ...this.sessionContext },
            metadata: {
                createdBy: this.syncManager.getNodeId(),
                ...metadata,
            },
        };
        this.snapshots.set(snapshot.id, snapshot);
        this.syncManager.createState(snapshot);
        return snapshot;
    }
    getSnapshot(id) {
        return this.snapshots.get(id);
    }
    listSnapshots() {
        return Array.from(this.snapshots.values())
            .sort((a, b) => b.timestamp - a.timestamp);
    }
    restoreSnapshot(id) {
        const snapshot = this.snapshots.get(id);
        if (!snapshot)
            return false;
        this.files = new Map(snapshot.files);
        this.sessionContext = { ...snapshot.context };
        return true;
    }
    getSyncManager() {
        return this.syncManager;
    }
    getSyncStatus() {
        return this.syncManager.getStatus();
    }
    async pushChanges() {
        const snapshot = this.createSnapshot({ description: 'Auto-push' });
        return { success: true, snapshot };
    }
    async pullChanges(remoteSnapshots) {
        const remoteStates = remoteSnapshots.map(snap => {
            const state = this.syncManager.getState(snap.id);
            if (state)
                return state;
            return {
                id: snap.id,
                data: snap,
                version: 1,
                timestamp: snap.timestamp,
                vectorClock: { [snap.metadata.createdBy]: 1 },
                hash: this.computeHash(JSON.stringify(snap)),
                lastModifiedBy: snap.metadata.createdBy,
            };
        });
        const result = await this.syncManager.reconcile(remoteStates);
        return {
            success: result.success,
            conflicts: result.conflicts,
            applied: result.reconciledStates.length,
        };
    }
    diffWith(other) {
        const diffs = [];
        const allPaths = new Set([
            ...this.files.keys(),
            ...other.files.keys(),
        ]);
        for (const p of allPaths) {
            const current = this.files.get(p);
            const snapshot = other.files.get(p);
            if (current && !snapshot) {
                diffs.push({ path: p, type: 'added', local: current.content });
            }
            else if (!current && snapshot) {
                diffs.push({ path: p, type: 'deleted', remote: snapshot.content });
            }
            else if (current && snapshot && current.hash !== snapshot.hash) {
                diffs.push({
                    path: p,
                    type: 'modified',
                    local: current.content,
                    remote: snapshot.content,
                });
            }
        }
        return diffs;
    }
    computeHash(content) {
        let hash = 0;
        for (let i = 0; i < content.length; i++) {
            const char = content.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return Math.abs(hash).toString(16).padStart(8, '0');
    }
    dispose() {
        this.files.clear();
        this.snapshots.clear();
        this.syncManager.dispose();
    }
}
// ============================================================================
// Sync Bindings
// ============================================================================
let workspaceTracker = null;
export async function getWorkspaceTracker(sessionId) {
    if (!workspaceTracker) {
        workspaceTracker = new WorkspaceStateTracker(sessionId || 'default');
        await workspaceTracker.initialize();
    }
    return workspaceTracker;
}
export function resetWorkspaceTracker() {
    if (workspaceTracker) {
        workspaceTracker.dispose();
        workspaceTracker = null;
    }
}
/**
 * Create sync bindings for the scripting runtime
 */
export async function createSyncBindings(config, print) {
    const tracker = await getWorkspaceTracker(config.sessionId);
    const sync = {};
    sync.status = () => {
        const status = tracker.getSyncStatus();
        const dirtyFiles = tracker.getDirtyFiles();
        const snapshots = tracker.listSnapshots();
        return JSON.stringify({
            status,
            dirtyFiles: dirtyFiles.length,
            totalTracked: tracker.getTrackedFiles().length,
            snapshots: snapshots.length,
            lastSnapshot: snapshots[0]?.timestamp || null,
        }, null, 2);
    };
    sync.snapshot = (name, description) => {
        const snapshot = tracker.createSnapshot({
            name: name || `Snapshot ${new Date().toISOString()}`,
            description,
        });
        print(`Created snapshot: ${snapshot.id}`);
        return snapshot.id;
    };
    sync.list = () => {
        const snapshots = tracker.listSnapshots();
        if (snapshots.length === 0) {
            return 'No snapshots available';
        }
        const lines = snapshots.map(s => {
            const date = new Date(s.timestamp).toLocaleString();
            const snapName = s.metadata.name || 'Unnamed';
            return `${s.id}: ${snapName} (${date}) - ${s.files.size} files`;
        });
        return lines.join('\n');
    };
    sync.restore = (snapshotId) => {
        const success = tracker.restoreSnapshot(snapshotId);
        if (success) {
            print(`Restored workspace from snapshot: ${snapshotId}`);
        }
        else {
            print(`Snapshot not found: ${snapshotId}`);
        }
        return success;
    };
    sync.push = async () => {
        const result = await tracker.pushChanges();
        print(`Pushed changes. Snapshot: ${result.snapshot.id}`);
        return result.snapshot.id;
    };
    sync.diff = (snapshotId) => {
        const snapshots = tracker.listSnapshots();
        if (snapshots.length === 0) {
            return 'No snapshots to compare with';
        }
        const targetSnapshot = snapshotId
            ? tracker.getSnapshot(snapshotId)
            : snapshots[0];
        if (!targetSnapshot) {
            return `Snapshot not found: ${snapshotId}`;
        }
        const diffs = tracker.diffWith(targetSnapshot);
        if (diffs.length === 0) {
            return 'No differences';
        }
        const lines = diffs.map(d => {
            switch (d.type) {
                case 'added': return `+ ${d.path}`;
                case 'deleted': return `- ${d.path}`;
                case 'modified': return `~ ${d.path}`;
            }
        });
        return lines.join('\n');
    };
    sync.track = (filePath, content) => {
        tracker.trackFile(filePath, content, true);
        print(`Tracking: ${filePath}`);
    };
    sync.untrack = (filePath) => {
        tracker.untrackFile(filePath);
        print(`Untracked: ${filePath}`);
    };
    sync.markDirty = (filePath) => {
        tracker.markFileDirty(filePath);
    };
    sync.files = () => {
        const files = tracker.getTrackedFiles();
        if (files.length === 0) {
            return 'No files tracked';
        }
        const lines = files.map(f => {
            const status = f.isDirty ? '[M]' : '[ ]';
            const open = f.isOpen ? '*' : ' ';
            return `${status}${open} ${f.path}`;
        });
        return lines.join('\n');
    };
    sync.context = () => {
        const ctx = tracker.getContext();
        return JSON.stringify(ctx, null, 2);
    };
    sync.setContext = (key, value) => {
        tracker.updateContext({ [key]: value });
    };
    sync.resolve = (conflictId, resolution) => {
        const manager = tracker.getSyncManager();
        const _resolutionMap = {
            local: 'local-wins',
            remote: 'remote-wins',
            merge: 'merge',
        };
        const states = manager.getAllStates();
        for (const state of states) {
            if (state.id === conflictId) {
                print(`Resolved conflict: ${conflictId} -> ${resolution}`);
                return true;
            }
        }
        print(`Conflict not found: ${conflictId}`);
        return false;
    };
    return { sync };
}
export default {
    createSyncBindings,
    getWorkspaceTracker,
    resetWorkspaceTracker,
    WorkspaceStateTracker,
};
//# sourceMappingURL=sync-bindings.js.map