/**
 * Remote Substrate — State Sync
 *
 * Mirrors remote task and health state into
 * local runtime store domains (AcpDomainState, TaskDomainState).
 *
 * The sync layer is a one-way bridge: remote data flows into local store
 * domains via the provided mutation callbacks. The store remains the
 * single source of truth for local rendering — remote state is a projection.
 */
import { logger } from '../../utils/logger.js';
// ── RemoteStateSyncer ─────────────────────────────────────────────────────────
/**
 * RemoteStateSyncer — applies incoming remote state snapshots into local domains.
 *
 * Owns the translation from `RemoteTask`/`RemoteHealth` types (remote-facing)
 * into `RuntimeTask` / ACP domain types (local store-facing).
 *
 * @example
 * ```ts
 * const syncer = new RemoteStateSyncer(storeCallbacks);
 *
 * // Called by the transport layer when a STATE_SNAPSHOT message arrives:
 * syncer.syncSnapshot(session, incomingTasks, incomingHealth);
 *
 * // Called for incremental task updates:
 * syncer.syncTaskUpdate(session, remoteTask);
 * ```
 */
export class RemoteStateSyncer {
    callbacks;
    constructor(callbacks) {
        this.callbacks = callbacks;
    }
    /**
     * Sync a full remote state snapshot into local store domains.
     *
     * Called when a STATE_SNAPSHOT data message arrives after reconnect sync.
     * Applies all tasks and the health status in a single pass.
     *
     * @param session - Current remote session (provides identity context).
     * @param tasks - Remote tasks from the snapshot.
     * @param health - Remote health snapshot.
     */
    syncSnapshot(session, tasks, health) {
        logger.debug('RemoteStateSyncer.syncSnapshot: applying state snapshot', {
            taskCount: tasks.length,
            healthStatus: health.status,
            sessionId: session.identity.sessionId,
        });
        for (const task of tasks) {
            this._applyRemoteTask(session, task);
        }
        this._applyHealth(session, health);
    }
    /**
     * Sync an incremental task update.
     *
     * Called when a TASK_UPDATE or TASK_SUBMIT data message arrives.
     *
     * @param session - Current remote session.
     * @param task - Updated remote task.
     */
    syncTaskUpdate(session, task) {
        this._applyRemoteTask(session, task);
    }
    /**
     * Sync an incremental health update.
     *
     * Called when a HEALTH_REPORT data message arrives.
     *
     * @param session - Current remote session.
     * @param health - Updated health snapshot.
     */
    syncHealthUpdate(session, health) {
        this._applyHealth(session, health);
    }
    /**
     * Sync transport state change into the ACP connection entry.
     *
     * Called by the reconnect engine on every state transition.
     *
     * @param session - Current remote session.
     */
    syncTransportState(session) {
        const { identity, transportState, lastError, reconnectAttempts, messagesSent, messagesReceived } = session;
        this.callbacks.updateAcpConnection(identity.agentId, {
            transportState,
            lastError,
            errorCount: reconnectAttempts,
            messageCount: messagesSent + messagesReceived,
        });
    }
    // ── Private helpers ──────────────────────────────────────────────────────────
    _applyRemoteTask(session, remote) {
        const isTerminal = (remote.status === 'completed' ||
            remote.status === 'failed' ||
            remote.status === 'cancelled');
        if (isTerminal) {
            const terminalStatus = remote.status;
            this.callbacks.finalizeRemoteTask(remote.taskId, terminalStatus, remote.error);
        }
        else {
            const runtimeTask = {
                id: remote.taskId,
                kind: 'acp',
                title: remote.title,
                status: remote.status,
                owner: remote.agentId,
                cancellable: remote.status === 'queued' || remote.status === 'running',
                childTaskIds: [],
                queuedAt: remote.updatedAt,
                startedAt: remote.status !== 'queued' ? remote.updatedAt : undefined,
                endedAt: undefined,
                error: remote.error,
                correlationId: session.identity.sessionId,
            };
            this.callbacks.upsertRemoteTask(runtimeTask);
        }
    }
    _applyHealth(session, health) {
        // Map remote health status to ACP transport state
        const transportState = (() => {
            switch (health.status) {
                case 'healthy': return 'connected';
                case 'degraded': return 'degraded';
                case 'unreachable': return 'reconnecting';
            }
        })();
        this.callbacks.updateAcpConnection(session.identity.agentId, {
            transportState,
            lastError: health.degradedReason,
        });
    }
}
// ── No-op sync callbacks (for testing or disabled sync) ──────────────────────
/**
 * Returns a no-op SyncStoreCallbacks implementation.
 * Useful when the store is not yet initialized or sync is disabled.
 */
export function createNoOpSyncCallbacks() {
    return {
        updateAcpConnection: (_agentId, _patch) => { },
        upsertRemoteTask: (_task) => { },
        finalizeRemoteTask: (_taskId, _status, _error) => { },
    };
}
// ── Utility: build initial AcpConnection for a remote session ─────────────────
/**
 * Build an initial AcpConnection domain entry from a remote session.
 *
 * Call this when registering a new remote connection with the ACP store domain.
 *
 * @param session - Remote session snapshot.
 * @param label - Human-readable connection label.
 * @returns An AcpConnection suitable for inserting into AcpDomainState.connections.
 */
export function buildAcpConnectionEntry(session, label) {
    return {
        agentId: session.identity.agentId,
        label,
        transportState: session.transportState,
        connectedAt: session.lastConnectedAt,
        completing: false,
        messageCount: session.messagesSent + session.messagesReceived,
        errorCount: session.reconnectAttempts,
        lastError: session.lastError,
        taskId: session.identity.taskId,
    };
}
// ── Utility: domain state accessors ──────────────────────────────────────────
/**
 * Count active remote connections in an ACP domain state snapshot.
 *
 * @param state - ACP domain state snapshot.
 * @returns Number of connections that are not yet completing.
 */
export function countActiveRemoteConnections(state) {
    return state.activeConnectionIds.length;
}
/**
 * Extract remote task IDs from a tasks domain snapshot.
 *
 * @param state - Tasks domain state snapshot.
 * @returns Array of task IDs whose kind === 'acp'.
 */
export function extractRemoteTaskIds(state) {
    const ids = [];
    for (const [id, task] of state.tasks) {
        if (task.kind === 'acp')
            ids.push(id);
    }
    return ids;
}
