import { logger } from '../../../utils/internal/logger.js';
import { idGenerator } from '../../../utils/security/idGenerator.js';
import { ATTR_MCP_TASK_STATUS, ATTR_MCP_TASK_STORE_TYPE } from '../../../utils/telemetry/attributes.js';
import { createCounter, createObservableGauge } from '../../../utils/telemetry/metrics.js';
import { SessionAwareTaskStore } from './sessionAwareTaskStore.js';
import { StorageBackedTaskStore } from './storageBackedTaskStore.js';
import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from './taskTypes.js';
// ---------------------------------------------------------------------------
// Task lifecycle metrics
// ---------------------------------------------------------------------------
let taskCreatedCounter;
let taskStatusCounter;
function getTaskMetrics() {
    taskCreatedCounter ??= createCounter('mcp.tasks.created', 'Total tasks created', '{tasks}');
    taskStatusCounter ??= createCounter('mcp.tasks.status_changes', 'Total task status transitions', '{transitions}');
    return { taskCreatedCounter, taskStatusCounter };
}
/**
 * Wraps a TaskStore to record lifecycle metrics on create, status update,
 * and result storage. Delegates all actual logic to the inner store.
 */
class InstrumentedTaskStore {
    inner;
    storeTypeLabel;
    constructor(inner, storeTypeLabel) {
        this.inner = inner;
        this.storeTypeLabel = storeTypeLabel;
    }
    async createTask(taskParams, requestId, request, sessionId) {
        const task = await this.inner.createTask(taskParams, requestId, request, sessionId);
        getTaskMetrics().taskCreatedCounter.add(1, {
            [ATTR_MCP_TASK_STORE_TYPE]: this.storeTypeLabel,
        });
        return task;
    }
    async storeTaskResult(taskId, status, result, sessionId) {
        await this.inner.storeTaskResult(taskId, status, result, sessionId);
        getTaskMetrics().taskStatusCounter.add(1, {
            [ATTR_MCP_TASK_STATUS]: status,
            [ATTR_MCP_TASK_STORE_TYPE]: this.storeTypeLabel,
        });
    }
    async updateTaskStatus(taskId, status, statusMessage, sessionId) {
        await this.inner.updateTaskStatus(taskId, status, statusMessage, sessionId);
        getTaskMetrics().taskStatusCounter.add(1, {
            [ATTR_MCP_TASK_STATUS]: status,
            [ATTR_MCP_TASK_STORE_TYPE]: this.storeTypeLabel,
        });
    }
    getTask(taskId, sessionId) {
        return this.inner.getTask(taskId, sessionId);
    }
    getTaskResult(taskId, sessionId) {
        return this.inner.getTaskResult(taskId, sessionId);
    }
    listTasks(cursor, sessionId) {
        return this.inner.listTasks(cursor, sessionId);
    }
}
/**
 * Singleton service that manages task state and message queues for the MCP server.
 *
 * The TaskManager provides:
 * - A shared TaskStore for creating, tracking, and completing tasks
 * - A shared TaskMessageQueue for side-channel message delivery
 * - Cleanup methods for graceful shutdown
 *
 * The store type is determined by configuration:
 * - `in-memory`: Fast, suitable for development (data lost on restart)
 * - `storage`: Persistent, uses configured StorageService backend
 *
 * @example
 * ```typescript
 * // Inject via DI
 * constructor(@inject(TaskManagerToken) private taskManager: TaskManager) {}
 *
 * // Access stores
 * const taskStore = this.taskManager.getTaskStore();
 * const messageQueue = this.taskManager.getMessageQueue();
 * ```
 *
 * @experimental
 */
export class TaskManager {
    taskStore;
    inMemoryTaskStore = null;
    messageQueue;
    storeType;
    isShuttingDown = false;
    constructor(config, storageService) {
        this.storeType = config.tasks.storeType;
        this.messageQueue = new InMemoryTaskMessageQueue();
        let baseStore;
        if (this.storeType === 'storage') {
            baseStore = new StorageBackedTaskStore(storageService, {
                tenantId: config.tasks.tenantId,
                defaultTtl: config.tasks.defaultTtlMs ?? null,
            });
        }
        else {
            this.inMemoryTaskStore = new InMemoryTaskStore();
            // Wrap with session ownership enforcement — the SDK's InMemoryTaskStore
            // ignores sessionId parameters, so SessionAwareTaskStore adds that layer.
            baseStore = new SessionAwareTaskStore(this.inMemoryTaskStore);
        }
        // Wrap with lifecycle metrics (outermost layer)
        this.taskStore = new InstrumentedTaskStore(baseStore, this.storeType);
        logger.info(`TaskManager initialized with ${this.storeType} task store`, {
            operation: 'TaskManager.constructor',
            requestId: idGenerator.generate('req'),
            timestamp: new Date().toISOString(),
            storeType: this.storeType,
            ...(this.storeType === 'storage' && { tenantId: config.tasks.tenantId }),
        });
        // Wire task count to OTel observable gauge
        if (this.inMemoryTaskStore) {
            const store = this.inMemoryTaskStore;
            createObservableGauge('mcp.tasks.active', 'Number of active tasks in the in-memory store', () => store.getAllTasks().length, '{tasks}');
        }
    }
    /**
     * Returns the TaskStore instance for managing task lifecycle.
     *
     * The TaskStore handles:
     * - Task creation with TTL and poll intervals
     * - Status updates (working, input_required, completed, failed, cancelled)
     * - Result storage and retrieval
     * - Task listing with pagination
     *
     * @returns The singleton TaskStore instance
     */
    getTaskStore() {
        return this.taskStore;
    }
    /**
     * Returns the TaskMessageQueue instance for side-channel messaging.
     *
     * The message queue enables:
     * - Queuing requests/notifications for delivery via tasks/result
     * - FIFO ordering per task
     * - Atomic enqueue with size limits
     *
     * @returns The singleton TaskMessageQueue instance
     */
    getMessageQueue() {
        return this.messageQueue;
    }
    /**
     * Returns the store type currently in use.
     *
     * @returns 'in-memory' or 'storage'
     */
    getStoreType() {
        return this.storeType;
    }
    /**
     * Performs cleanup of task resources.
     *
     * Should be called during graceful server shutdown to:
     * - Cancel cleanup timers in the in-memory task store
     * - Clear any pending message queues
     *
     * Note: Storage-backed task stores don't require cleanup as data persists.
     *
     * @param context - Request context for logging
     */
    cleanup(context) {
        if (this.isShuttingDown) {
            return;
        }
        this.isShuttingDown = true;
        const logContext = context ?? {
            operation: 'TaskManager.cleanup',
            requestId: idGenerator.generate('req'),
            timestamp: new Date().toISOString(),
        };
        logger.info('Cleaning up TaskManager resources...', logContext);
        // Only InMemoryTaskStore has cleanup timers
        if (this.inMemoryTaskStore) {
            this.inMemoryTaskStore.cleanup();
        }
        logger.info('TaskManager cleanup complete', logContext);
    }
    /**
     * Returns the current task count (for debugging/monitoring).
     * Only available for in-memory store; returns `null` for storage-backed store.
     *
     * @returns The number of tasks currently tracked, or `null` if unavailable
     */
    getTaskCount() {
        if (this.inMemoryTaskStore) {
            return this.inMemoryTaskStore.getAllTasks().length;
        }
        // Storage-backed store doesn't have getAllTasks - would require listing
        return null;
    }
    /**
     * Checks if the TaskManager is shutting down.
     *
     * @returns True if cleanup has been initiated
     */
    isCleaningUp() {
        return this.isShuttingDown;
    }
}
//# sourceMappingURL=taskManager.js.map