/**
 * Diagnostics action system — action dispatch bindings for diagnostic entries.
 *
 * Defines the action types, permission model, and dispatcher that allow
 * one-click remediation from the diagnostics UI. Actions are attached to
 * high-severity diagnostic entries and dispatched through this system,
 * which validates permissions before delegating to the appropriate handler.
 *
 * Supported action targets:
 *  - Load replay: loads a forensics run into the replay engine
 *  - Run policy simulation: triggers a simulation pass for a tool call
 *  - Jump to task: navigates the UI focus to a related task entry
 *  - Jump to agent: navigates the UI focus to a related agent entry
 *  - Jump to tool call: navigates the UI focus to a related tool call entry
 */
import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
// ── Default permission checker ────────────────────────────────────────────────
const DEFAULT_PERMISSION_CHECKER = (required) => {
    return required !== 'admin';
};
// ── Dispatcher ────────────────────────────────────────────────────────────────
/**
 * DiagnosticActionDispatcher — executes diagnostic entry actions.
 *
 * Routes incoming DiagnosticAction values to the appropriate handler,
 * performing a permission check before dispatch. All errors are caught
 * and converted to ActionResult failures so callers never receive thrown
 * exceptions from this API.
 *
 * Usage:
 * ```ts
 * const dispatcher = new DiagnosticActionDispatcher({
 *   replayEngine,
 *   simulator,
 *   controlPlane,
 *   navigateTo: (target, id) => focusPanel(target, id),
 * });
 *
 * const result = await dispatcher.dispatch(action);
 * if (!result.success) {
 *   showError(result.message);
 * }
 * ```
 */
export class DiagnosticActionDispatcher {
    _replayEngine;
    _simulator;
    _controlPlane;
    _navigateTo;
    _checkPermission;
    constructor(config = {}) {
        this._replayEngine = config.replayEngine;
        this._simulator = config.simulator;
        this._controlPlane = config.controlPlane;
        this._navigateTo = config.navigateTo;
        this._checkPermission = config.checkPermission ?? DEFAULT_PERMISSION_CHECKER;
    }
    /**
     * Dispatch a diagnostic action.
     *
     * Performs a permission check, then delegates to the appropriate handler.
     * All handler errors are caught and returned as failure ActionResults.
     *
     * @param action - The action to execute.
     * @returns An ActionResult describing the outcome.
     */
    async dispatch(action) {
        // Permission gate
        if (!this._checkPermission(action.permission)) {
            logger.debug('[DiagnosticActionDispatcher] permission denied', {
                type: action.type,
                required: action.permission,
            });
            return {
                success: false,
                message: `Permission denied: '${action.permission}' access required for action '${action.type}'.`,
                permissionDenied: true,
            };
        }
        try {
            return await this._route(action);
        }
        catch (err) {
            const message = summarizeError(err);
            logger.debug('[DiagnosticActionDispatcher] action error', {
                type: action.type,
                err: message,
            });
            return {
                success: false,
                message: `Action '${action.type}' failed: ${message}`,
            };
        }
    }
    async _route(action) {
        switch (action.type) {
            case 'load-replay':
                return this._handleLoadReplay(action.payload);
            case 'run-policy-simulation':
                return this._handlePolicySimulation(action.payload);
            case 'jump-to-task':
                return this._handleJump('task', action.payload.taskId);
            case 'jump-to-agent':
                return this._handleJump('agent', action.payload.agentId);
            case 'jump-to-tool-call':
                return this._handleJump('tool-call', action.payload.callId);
            case 'retry-task':
                return this._handleRetryTask(action.payload);
            case 'cancel-task':
                return this._handleCancelTask(action.payload);
            case 'cancel-agent':
                return this._handleCancelAgent(action.payload);
            default: {
                // Exhaustiveness guard — TypeScript will error if a case is missing.
                const exhaustive = action;
                return {
                    success: false,
                    message: `Unknown action type: ${String(exhaustive)}`,
                };
            }
        }
    }
    // ── Handlers ────────────────────────────────────────────────────────────────
    _handleLoadReplay(payload) {
        if (!this._replayEngine) {
            return {
                success: false,
                message: 'Replay engine is not available. Attach a DeterministicReplayEngine to the dispatcher.',
            };
        }
        // Reset the engine to 'idle'. The caller is expected to then fetch
        // ledger entries for the given runId and call engine.load(). The
        // ReplayPanel will surface the updated state via subscriber notifications.
        this._replayEngine.reset();
        logger.debug('[DiagnosticActionDispatcher] load-replay requested', {
            runId: payload.runId,
        });
        return {
            success: true,
            message: `Replay engine reset for run '${payload.runId}'. Load the ledger entries to begin stepping.`,
        };
    }
    _handlePolicySimulation(payload) {
        if (!this._simulator) {
            return {
                success: false,
                message: 'Policy simulator is not available. Attach a PermissionSimulator to the dispatcher.',
            };
        }
        const result = this._simulator.evaluate(payload.toolName, payload.args);
        const diverged = result.diverged;
        const actual = result.actualDecision.allowed ? 'allowed' : 'denied';
        const simulated = result.simulatedDecision.allowed ? 'allowed' : 'denied';
        logger.debug('[DiagnosticActionDispatcher] policy simulation complete', {
            toolName: payload.toolName,
            diverged,
            actual,
            simulated,
        });
        if (diverged) {
            return {
                success: true,
                message: `Policy simulation diverged for '${payload.toolName}': actual=${actual}, simulated=${simulated}.`,
            };
        }
        return {
            success: true,
            message: `Policy simulation for '${payload.toolName}': both evaluators returned ${actual}.`,
        };
    }
    _handleJump(target, id) {
        if (!this._navigateTo) {
            return {
                success: true,
                message: `Jump to ${target} '${id}' requested but no navigation handler is registered.`,
            };
        }
        this._navigateTo(target, id);
        return {
            success: true,
            message: `Navigated to ${target} '${id}'.`,
        };
    }
    _handleRetryTask(payload) {
        if (!this._controlPlane) {
            return {
                success: false,
                message: 'Ops control plane is not available. Attach an OpsControlPlane to the dispatcher.',
            };
        }
        if (!this._controlPlane.canRetryTask(payload.taskId)) {
            return {
                success: false,
                message: `Task '${payload.taskId}' cannot be retried in its current state.`,
            };
        }
        this._controlPlane.retryTask(payload.taskId, payload.note);
        return {
            success: true,
            message: `Task '${payload.taskId}' queued for retry.`,
        };
    }
    _handleCancelTask(payload) {
        if (!this._controlPlane) {
            return {
                success: false,
                message: 'Ops control plane is not available. Attach an OpsControlPlane to the dispatcher.',
            };
        }
        if (!this._controlPlane.canCancelTask(payload.taskId)) {
            return {
                success: false,
                message: `Task '${payload.taskId}' cannot be cancelled in its current state.`,
            };
        }
        this._controlPlane.cancelTask(payload.taskId, payload.note);
        return {
            success: true,
            message: `Task '${payload.taskId}' cancelled.`,
        };
    }
    _handleCancelAgent(payload) {
        if (!this._controlPlane) {
            return {
                success: false,
                message: 'Ops control plane is not available. Attach an OpsControlPlane to the dispatcher.',
            };
        }
        if (!this._controlPlane.canCancelAgent(payload.agentId)) {
            return {
                success: false,
                message: `Agent '${payload.agentId}' cannot be cancelled in its current state.`,
            };
        }
        this._controlPlane.cancelAgent(payload.agentId, payload.note);
        return {
            success: true,
            message: `Agent '${payload.agentId}' cancelled.`,
        };
    }
}
// ── Factory helpers ───────────────────────────────────────────────────────────
/**
 * Build a 'load-replay' action for a forensics run ID.
 */
export function buildLoadReplayAction(runId) {
    return {
        label: 'Load Replay',
        type: 'load-replay',
        permission: 'operator',
        payload: { runId },
    };
}
/**
 * Build a 'run-policy-simulation' action for a tool call.
 */
export function buildRunPolicySimulationAction(toolName, args) {
    return {
        label: 'Run Policy Simulation',
        type: 'run-policy-simulation',
        permission: 'operator',
        payload: { toolName, args },
    };
}
/**
 * Build a 'jump-to-task' action.
 */
export function buildJumpToTaskAction(taskId) {
    return {
        label: 'Jump to Task',
        type: 'jump-to-task',
        permission: 'read',
        payload: { taskId },
    };
}
/**
 * Build a 'jump-to-agent' action.
 */
export function buildJumpToAgentAction(agentId) {
    return {
        label: 'Jump to Agent',
        type: 'jump-to-agent',
        permission: 'read',
        payload: { agentId },
    };
}
/**
 * Build a 'jump-to-tool-call' action.
 */
export function buildJumpToToolCallAction(callId) {
    return {
        label: 'Jump to Tool Call',
        type: 'jump-to-tool-call',
        permission: 'read',
        payload: { callId },
    };
}
/**
 * Build a 'retry-task' action.
 */
export function buildRetryTaskAction(taskId, note) {
    return {
        label: 'Retry Task',
        type: 'retry-task',
        permission: 'operator',
        payload: { taskId, note },
    };
}
/**
 * Build a 'cancel-task' action.
 */
export function buildCancelTaskAction(taskId, note) {
    return {
        label: 'Cancel Task',
        type: 'cancel-task',
        permission: 'operator',
        payload: { taskId, note },
    };
}
/**
 * Build a 'cancel-agent' action.
 */
export function buildCancelAgentAction(agentId, note) {
    return {
        label: 'Cancel Agent',
        type: 'cancel-agent',
        permission: 'operator',
        payload: { agentId, note },
    };
}
/**
 * Create a HighSeverityDiagnostic from a task failure.
 *
 * Attaches retry and jump-to-task actions. Satisfies the acceptance criterion
 * that every high-severity diagnostic has at least one remediation action.
 */
export function diagnosticFromTaskFailure(opts) {
    return {
        id: `task-failure:${opts.taskId}`,
        summary: `Task failed: ${opts.description} — ${opts.error}`,
        domain: 'tasks',
        severity: 'error',
        ts: opts.ts,
        sessionId: opts.sessionId,
        traceId: opts.traceId,
        actions: [
            buildRetryTaskAction(opts.taskId, 'Diagnostic retry'),
            buildJumpToTaskAction(opts.taskId),
        ],
    };
}
/**
 * Create a HighSeverityDiagnostic from an agent failure.
 *
 * Attaches cancel and jump-to-agent actions.
 */
export function diagnosticFromAgentFailure(opts) {
    return {
        id: `agent-failure:${opts.agentId}`,
        summary: `Agent failed: ${opts.task} — ${opts.error}`,
        domain: 'agents',
        severity: 'error',
        ts: opts.ts,
        sessionId: opts.sessionId,
        traceId: opts.traceId,
        actions: [
            buildCancelAgentAction(opts.agentId, 'Diagnostic cancel'),
            buildJumpToAgentAction(opts.agentId),
        ],
    };
}
/**
 * Create a HighSeverityDiagnostic from a tool contract violation.
 *
 * Attaches a policy simulation action and optionally a jump-to-tool-call action.
 */
export function diagnosticFromToolContractViolation(opts) {
    const extra = opts.callId
        ? [buildJumpToToolCallAction(opts.callId)]
        : [];
    const actions = [
        buildRunPolicySimulationAction(opts.toolName, {}),
        ...extra,
    ];
    return {
        id: `tool-contract:${opts.toolName}:${opts.ts}`,
        summary: `Tool contract violation: ${opts.toolName} — ${opts.message}`,
        domain: 'tool-contracts',
        severity: 'error',
        ts: opts.ts,
        sessionId: opts.sessionId,
        traceId: opts.traceId,
        actions,
    };
}
/**
 * Create a HighSeverityDiagnostic from a forensics/replay run.
 *
 * Attaches a load-replay action so the operator can step through the run.
 */
export function diagnosticFromForensicsRun(opts) {
    return {
        id: `forensics:${opts.runId}`,
        summary: opts.summary,
        domain: 'forensics',
        severity: 'error',
        ts: opts.ts,
        sessionId: opts.sessionId,
        traceId: opts.traceId,
        actions: [buildLoadReplayAction(opts.runId)],
    };
}
