/**
 * Predecessor Query Service
 *
 * This service enables agents to consult previous sessions for context and guidance.
 * It implements the predecessor query pattern from the orchestration spec:
 *
 * 1. Find most recent session for a given role
 * 2. Resume that previous session using Claude Code's --resume flag
 * 3. Send a message to the predecessor
 * 4. Capture the response
 * 5. Suspend the predecessor session again
 * 6. Return the response to the current agent
 *
 * This allows collaborative problem-solving between agents by enabling new sessions
 * to query previous session holders for historical context.
 *
 * @module
 */
import { EventEmitter } from 'node:events';
import { createTimestamp } from '@stoneforge/core';
// ============================================================================
// Constants
// ============================================================================
/** Default timeout for predecessor queries (60 seconds) */
export const DEFAULT_QUERY_TIMEOUT_MS = 60000;
/** Minimum timeout for predecessor queries (10 seconds) */
export const MIN_QUERY_TIMEOUT_MS = 10000;
/** Maximum timeout for predecessor queries (5 minutes) */
export const MAX_QUERY_TIMEOUT_MS = 300000;
// ============================================================================
// Implementation
// ============================================================================
/**
 * Implementation of the Predecessor Query Service.
 */
export class PredecessorQueryServiceImpl {
    sessionManager;
    activeQueries = new Map();
    queryCounter = 0;
    constructor(sessionManager) {
        this.sessionManager = sessionManager;
    }
    async consultPredecessor(requestingAgentId, role, message, options) {
        const startTime = Date.now();
        const timeout = this.normalizeTimeout(options?.timeout);
        // Find the predecessor
        const predecessor = await this.getPredecessorInfo(role);
        if (!predecessor) {
            return {
                success: false,
                error: `No predecessor found for role: ${role}`,
                completedAt: createTimestamp(),
                durationMs: Date.now() - startTime,
            };
        }
        // Create the active query record
        const queryId = this.generateQueryId();
        const query = {
            id: queryId,
            requestingAgentId,
            targetRole: role,
            message,
            status: 'pending',
            startedAt: createTimestamp(),
            responseAccumulator: '',
            events: new EventEmitter(),
        };
        this.activeQueries.set(queryId, query);
        try {
            // Build the prompt with context if provided
            let prompt = message;
            if (options?.context) {
                prompt = `Context: ${options.context}\n\nQuestion: ${message}`;
            }
            // Resume the predecessor session
            query.status = 'resuming';
            const { session, events } = await this.sessionManager.resumeSession(predecessor.agentId, {
                providerSessionId: predecessor.providerSessionId,
                workingDirectory: options?.workingDirectory ?? predecessor.workingDirectory,
                resumePrompt: prompt,
                checkReadyQueue: false, // Don't check UWP for predecessor queries
            });
            query.predecessorSessionId = session.id;
            query.status = 'waiting_response';
            // Set up response capture
            const responsePromise = this.captureResponse(query, events, timeout);
            // Wait for response with timeout
            const response = await responsePromise;
            // Suspend the predecessor session if requested (default: true)
            if (options?.suspendAfterResponse !== false && session.id) {
                await this.sessionManager.suspendSession(session.id, 'Predecessor query completed');
            }
            // Mark query as completed
            query.status = 'completed';
            query.completedAt = createTimestamp();
            return {
                success: true,
                response,
                predecessor,
                completedAt: query.completedAt,
                durationMs: Date.now() - startTime,
            };
        }
        catch (error) {
            // Mark query as failed
            query.status = error instanceof TimeoutError ? 'timed_out' : 'failed';
            query.completedAt = createTimestamp();
            // Try to clean up the predecessor session if it was started
            if (query.predecessorSessionId) {
                try {
                    await this.sessionManager.suspendSession(query.predecessorSessionId, `Predecessor query ${query.status}`);
                }
                catch {
                    // Ignore cleanup errors
                }
            }
            return {
                success: false,
                error: error instanceof Error ? error.message : String(error),
                predecessor,
                completedAt: query.completedAt,
                durationMs: Date.now() - startTime,
            };
        }
        finally {
            // Remove from active queries after a short delay to allow status checks
            setTimeout(() => {
                this.activeQueries.delete(queryId);
            }, 5000);
        }
    }
    async getPredecessorInfo(role) {
        const previousSession = await this.sessionManager.getPreviousSession(role);
        if (!previousSession) {
            return undefined;
        }
        // Must have a provider session ID to be resumable
        if (!previousSession.providerSessionId) {
            return undefined;
        }
        return {
            agentId: previousSession.agentId,
            agentName: previousSession.agentName,
            role: previousSession.role,
            providerSessionId: previousSession.providerSessionId,
            sessionId: previousSession.id,
            workingDirectory: previousSession.workingDirectory,
            originalStartedAt: previousSession.startedAt,
            suspendedAt: previousSession.endedAt,
        };
    }
    async hasPredecessor(role) {
        const info = await this.getPredecessorInfo(role);
        return info !== undefined;
    }
    getActiveQuery(queryId) {
        return this.activeQueries.get(queryId);
    }
    listActiveQueries() {
        return Array.from(this.activeQueries.values());
    }
    async cancelQuery(queryId) {
        const query = this.activeQueries.get(queryId);
        if (!query) {
            return;
        }
        // Mark as failed
        query.status = 'failed';
        query.completedAt = createTimestamp();
        // Emit cancel event
        query.events.emit('cancelled');
        // Try to suspend the predecessor session if it was started
        if (query.predecessorSessionId) {
            try {
                await this.sessionManager.suspendSession(query.predecessorSessionId, 'Predecessor query cancelled');
            }
            catch {
                // Ignore cleanup errors
            }
        }
        // Remove from active queries
        this.activeQueries.delete(queryId);
    }
    // ----------------------------------------
    // Private Helpers
    // ----------------------------------------
    generateQueryId() {
        this.queryCounter++;
        const timestamp = Date.now().toString(36);
        const counter = this.queryCounter.toString(36).padStart(4, '0');
        const random = Math.random().toString(36).slice(2, 6);
        return `pq-${timestamp}-${counter}-${random}`;
    }
    normalizeTimeout(timeout) {
        if (timeout === undefined) {
            return DEFAULT_QUERY_TIMEOUT_MS;
        }
        return Math.min(Math.max(timeout, MIN_QUERY_TIMEOUT_MS), MAX_QUERY_TIMEOUT_MS);
    }
    captureResponse(query, events, timeout) {
        return new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
                cleanup();
                reject(new TimeoutError(`Predecessor query timed out after ${timeout}ms`));
            }, timeout);
            const cleanup = () => {
                clearTimeout(timer);
                events.off('event', onEvent);
                events.off('exit', onExit);
                events.off('error', onError);
                query.events.off('cancelled', onCancelled);
            };
            const onEvent = (event) => {
                // Accumulate assistant messages
                if (event.type === 'assistant' && event.message) {
                    query.responseAccumulator += event.message;
                }
                // Check for result event (indicates completion)
                if (event.type === 'result') {
                    cleanup();
                    resolve(query.responseAccumulator.trim());
                }
                // Check for error event
                if (event.type === 'error') {
                    cleanup();
                    reject(new Error(event.raw.error ?? 'Unknown error from predecessor'));
                }
            };
            const onExit = (code, _signal) => {
                cleanup();
                if (query.responseAccumulator.trim()) {
                    resolve(query.responseAccumulator.trim());
                }
                else {
                    reject(new Error(`Predecessor session exited with code ${code} before responding`));
                }
            };
            const onError = (error) => {
                cleanup();
                reject(error);
            };
            const onCancelled = () => {
                cleanup();
                reject(new Error('Query was cancelled'));
            };
            events.on('event', onEvent);
            events.on('exit', onExit);
            events.on('error', onError);
            query.events.on('cancelled', onCancelled);
        });
    }
}
// ============================================================================
// Error Classes
// ============================================================================
/**
 * Error thrown when a predecessor query times out
 */
export class TimeoutError extends Error {
    constructor(message) {
        super(message);
        this.name = 'TimeoutError';
    }
}
/**
 * Error thrown when no predecessor is found for a role
 */
export class NoPredecessorError extends Error {
    constructor(role) {
        super(`No predecessor found for role: ${role}`);
        this.name = 'NoPredecessorError';
    }
}
// ============================================================================
// Factory Function
// ============================================================================
/**
 * Creates a PredecessorQueryService instance
 */
export function createPredecessorQueryService(sessionManager) {
    return new PredecessorQueryServiceImpl(sessionManager);
}
//# sourceMappingURL=predecessor-query.js.map