import { z } from 'zod';
// Shared diff logic for agents
function getAgentsToSync(currentAgents, previousAgentIds, projectId, logger) {
    const agentsToCreate = [];
    const currentAgentIds = new Set();
    for (const agent of currentAgents || []) {
        currentAgentIds.add(agent.id);
        // If ID is not in previous, add to create
        if (!previousAgentIds.has(agent.id)) {
            logger.debug('[CLI AGENT SYNC] Preparing to create: id="%s", name="%s"', agent.id, agent.name);
            agentsToCreate.push({
                id: agent.id,
                name: agent.name,
                agentId: agent.agentId,
                description: agent.description,
                version: agent.version,
                filename: agent.filename,
                projectId,
            });
        }
    }
    // If ID is in previous but not in current, add to delete
    const agentsToDelete = [];
    for (const previousId of previousAgentIds) {
        if (!currentAgentIds.has(previousId)) {
            logger.debug('[CLI AGENT SYNC] Preparing to delete: id="%s"', previousId);
            agentsToDelete.push(previousId);
        }
    }
    return { create: agentsToCreate, delete: agentsToDelete };
}
// Shared diff logic for evals
function getEvalsToSync(currentMetadata, previousEvalIds, projectId, logger) {
    const evalsToCreate = [];
    const currentEvalIds = new Set();
    for (const agent of currentMetadata.agents || []) {
        if (agent.evals) {
            for (const evalItem of agent.evals) {
                currentEvalIds.add(evalItem.id);
                // If ID is not in previous, add to create
                if (!previousEvalIds.has(evalItem.id)) {
                    logger.debug('[CLI EVAL SYNC] Preparing to create: id="%s", name="%s"', evalItem.id, evalItem.name);
                    evalsToCreate.push({
                        ...evalItem,
                        identifier: evalItem.identifier,
                        projectId,
                        agentIdentifier: agent.agentId,
                    });
                }
            }
        }
    }
    // If ID is in previous but not in current, add to delete
    const evalsToDelete = [];
    for (const previousId of previousEvalIds) {
        if (!currentEvalIds.has(previousId)) {
            logger.debug('[CLI EVAL SYNC] Preparing to delete: id="%s"', previousId);
            evalsToDelete.push(previousId);
        }
    }
    return { create: evalsToCreate, delete: evalsToDelete };
}
class DevmodeSyncService {
    logger;
    apiClient;
    constructor(logger, apiClient) {
        this.logger = logger;
        this.apiClient = apiClient;
    }
    async sync(currentMetadata, previousMetadata, projectId, deploymentId) {
        this.logger.debug('[CLI SYNC] sync() called with projectId=%s, deploymentId=%s', projectId, deploymentId);
        this.logger.debug('[CLI SYNC] currentMetadata has %d agents', currentMetadata.agents?.length ?? 0);
        // Build previous agent IDs set
        const previousAgentIds = new Set();
        if (previousMetadata) {
            this.logger.debug('[CLI SYNC] Previous metadata found with %d agent(s)', previousMetadata.agents?.length ?? 0);
            for (const agent of previousMetadata.agents || []) {
                previousAgentIds.add(agent.id);
            }
        }
        else {
            this.logger.debug('[CLI SYNC] No previous metadata, all agents will be treated as new');
        }
        // Build previous eval IDs set
        const previousEvalIds = new Set();
        if (previousMetadata) {
            let prevEvalCount = 0;
            for (const agent of previousMetadata.agents || []) {
                if (agent.evals) {
                    for (const evalItem of agent.evals) {
                        previousEvalIds.add(evalItem.id);
                        prevEvalCount++;
                    }
                }
            }
            this.logger.debug('Previous metadata found with %d evaluations', prevEvalCount);
        }
        else {
            this.logger.debug('No previous metadata, all evals will be treated as new');
        }
        const currentAgents = currentMetadata.agents || [];
        this.logger.debug('Processing %d current agent(s)', currentAgents.length);
        let currentEvalCount = 0;
        for (const agent of currentMetadata.agents || []) {
            if (agent.evals) {
                currentEvalCount += agent.evals.length;
                this.logger.debug('[CLI EVAL SYNC] Agent "%s" has %d evaluations', agent.name, agent.evals.length);
                for (const evalItem of agent.evals) {
                    this.logger.debug('[CLI EVAL SYNC]   - %s (id: %s, identifier: %s)', evalItem.name, evalItem.id, evalItem.identifier);
                }
            }
        }
        this.logger.debug('[CLI EVAL SYNC] Total current evaluations: %d', currentEvalCount);
        // Get agents and evals to sync using shared diff logic
        const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(currentAgents, previousAgentIds, projectId, this.logger);
        const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(currentMetadata, previousEvalIds, projectId, this.logger);
        if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
            this.logger.debug('Bulk syncing %d agent(s) to create, %d agent(s) to delete', agentsToCreate.length, agentsToDelete.length);
        }
        this.logger.debug('[CLI EVAL SYNC] Evals to sync: %d to create, %d to delete', evalsToCreate.length, evalsToDelete.length);
        // Sync both in parallel
        try {
            await Promise.all([
                this.syncAgents(agentsToCreate, agentsToDelete, deploymentId),
                this.syncEvals(evalsToCreate, evalsToDelete, deploymentId),
            ]);
            if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
                this.logger.debug('Successfully bulk synced %d agent(s) to create, %d agent(s) to delete', agentsToCreate.length, agentsToDelete.length);
            }
            if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
                this.logger.debug('Successfully bulk synced %d evaluations to create, %d evaluations to delete', evalsToCreate.length, evalsToDelete.length);
            }
        }
        catch (error) {
            this.logger.error('Failed to bulk sync agents/evals: %s', error);
            if (error instanceof Error) {
                this.logger.error('Error details: %s', error.message);
            }
            throw error;
        }
    }
    async syncAgents(agents, agentsToDelete, deploymentId) {
        if (agents.length === 0 && agentsToDelete.length === 0) {
            return;
        }
        const payload = {
            create: agents,
            delete: agentsToDelete,
            deploymentId,
        };
        this.logger.trace('[CLI AGENT SYNC] Sending payload to POST /cli/devmode/agent: %s', JSON.stringify(payload, null, 2));
        await this.apiClient.post('/cli/devmode/agent', payload, z.object({ success: z.boolean() }));
    }
    async syncEvals(evals, evalsToDelete, deploymentId) {
        if (evals.length === 0 && evalsToDelete.length === 0) {
            return;
        }
        const payload = {
            deploymentId,
            create: evals,
            delete: evalsToDelete,
        };
        this.logger.debug('[CLI EVAL SYNC] Sending payload to POST /cli/devmode/eval: %s', JSON.stringify(payload, null, 2));
        try {
            await this.apiClient.post('/cli/devmode/eval', payload, z.object({ success: z.boolean() }));
        }
        catch (error) {
            this.logger.error('[CLI EVAL SYNC] Sync failed: %s', error);
            throw error;
        }
    }
}
class MockDevmodeSyncService {
    logger;
    constructor(logger) {
        this.logger = logger;
    }
    async sync(currentMetadata, previousMetadata, projectId, deploymentId) {
        // Build previous agent IDs set
        this.logger.debug('Mock syncing agents and evals for deploymentId: %s', deploymentId);
        const previousAgentIds = new Set();
        if (previousMetadata) {
            for (const agent of previousMetadata.agents || []) {
                previousAgentIds.add(agent.id);
            }
        }
        // Build previous eval IDs set
        const previousEvalIds = new Set();
        if (previousMetadata) {
            for (const agent of previousMetadata.agents || []) {
                if (agent.evals) {
                    for (const evalItem of agent.evals) {
                        previousEvalIds.add(evalItem.id);
                    }
                }
            }
        }
        // Get agents and evals to sync using shared diff logic
        const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(currentMetadata.agents, previousAgentIds, projectId, this.logger);
        const { create: evalsToCreate, delete: evalsToDelete } = getEvalsToSync(currentMetadata, previousEvalIds, projectId, this.logger);
        // Log the requests that would be made
        if (agentsToCreate.length > 0 || agentsToDelete.length > 0) {
            this.logger.debug('[MOCK] Would make request: POST /cli/devmode/agent with %d agent(s) to create, %d agent(s) to delete', agentsToCreate.length, agentsToDelete.length);
            this.logger.debug('[MOCK] Request payload: %s', JSON.stringify({ create: agentsToCreate, delete: agentsToDelete }, null, 2));
        }
        if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
            this.logger.debug('[MOCK] Would make request: POST /cli/devmode/eval with %d evaluations to create, %d evaluations to delete', evalsToCreate.length, evalsToDelete.length);
            this.logger.debug('[MOCK] Request payload: %s', JSON.stringify({ create: evalsToCreate, delete: evalsToDelete }, null, 2));
        }
        if (agentsToCreate.length === 0 &&
            agentsToDelete.length === 0 &&
            evalsToCreate.length === 0 &&
            evalsToDelete.length === 0) {
            this.logger.debug('[MOCK] No requests would be made (no changes detected)');
        }
    }
}
export function createDevmodeSyncService({ logger, apiClient, mock = false, }) {
    if (mock) {
        return new MockDevmodeSyncService(logger);
    }
    if (!apiClient) {
        throw new Error('apiClient is required when not in mock mode');
    }
    return new DevmodeSyncService(logger, apiClient);
}
//# sourceMappingURL=sync.js.map