// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Vorion LLC
/**
 * REPLAY - Intent Replay System
 *
 * Re-executes or simulates past intents for:
 * - Debugging issues
 * - Policy testing ("what-if" scenarios)
 * - Compliance audits
 * - Training and demonstrations
 *
 * @packageDocumentation
 */
import { randomUUID } from 'node:crypto';
import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api';
import { createLogger } from '../../common/logger.js';
import { createSnapshotManager, } from './snapshot.js';
import { createReplayComparator, } from './comparator.js';
import { createSimulationEngine, } from './simulator.js';
const logger = createLogger({ component: 'replay-engine' });
// Tracer for replay operations
const TRACER_NAME = 'vorion.replay';
const tracer = trace.getTracer(TRACER_NAME, '1.0.0');
/**
 * Simple policy evaluation for replay
 */
function evaluatePolicies(intent, policies, trust) {
    const policiesApplied = [];
    let finalAction = 'allow';
    let finalReason;
    const actionPriority = {
        deny: 1,
        escalate: 2,
        limit: 3,
        monitor: 4,
        terminate: 5,
        allow: 6,
    };
    for (const policy of policies) {
        // Check target matching
        const intentTypeMatch = !policy.definition.target?.intentTypes?.length ||
            policy.definition.target.intentTypes.includes(intent.intentType ?? 'generic');
        const trustMatch = !policy.definition.target?.trustLevels?.length ||
            policy.definition.target.trustLevels.includes(trust.level);
        if (!intentTypeMatch || !trustMatch) {
            continue;
        }
        // Find matching rules
        for (const rule of policy.definition.rules) {
            if (!rule.enabled)
                continue;
            // Simplified rule matching
            const matched = true; // In real implementation, evaluate conditions
            if (matched) {
                policiesApplied.push({
                    policyId: policy.id,
                    policyName: policy.name,
                    action: rule.then.action,
                    reason: rule.then.reason,
                });
                if (actionPriority[rule.then.action] < actionPriority[finalAction]) {
                    finalAction = rule.then.action;
                    finalReason = rule.then.reason;
                }
                break; // First matching rule per policy
            }
        }
    }
    // If no rules matched, use default action
    if (policiesApplied.length === 0 && policies.length > 0) {
        const defaultAction = policies[0]?.definition.defaultAction ?? 'allow';
        policiesApplied.push({
            policyId: policies[0]?.id ?? 'default',
            policyName: policies[0]?.name ?? 'Default',
            action: defaultAction,
            reason: 'Default action applied',
        });
        finalAction = defaultAction;
        finalReason = 'Default action applied';
    }
    return {
        action: finalAction,
        reason: finalReason,
        policiesApplied,
    };
}
/**
 * ReplayEngine - Main class for replaying intent executions
 */
export class ReplayEngine {
    snapshotManager;
    comparator;
    simulator;
    constructor(snapshotManager, comparator, simulator) {
        this.snapshotManager = snapshotManager ?? createSnapshotManager();
        this.comparator = comparator ?? createReplayComparator();
        this.simulator = simulator ?? createSimulationEngine();
    }
    /**
     * Replay a past intent execution
     */
    async replay(intentId, options = {}) {
        return tracer.startActiveSpan('replay.execute', { kind: SpanKind.INTERNAL }, async (span) => {
            const startTime = Date.now();
            const replayId = randomUUID();
            const steps = [];
            const stepTiming = {
                restore: 0,
                'trust-evaluation': 0,
                'policy-evaluation': 0,
                decision: 0,
                execution: 0,
                complete: 0,
            };
            try {
                span.setAttributes({
                    'replay.id': replayId,
                    'replay.intent_id': intentId,
                    'replay.dry_run': options.dryRun ?? false,
                    'replay.stop_at': options.stopAt ?? 'complete',
                });
                // Step 1: Restore snapshot
                const restoreStart = Date.now();
                const snapshot = await this.snapshotManager.getByIntentId(intentId);
                if (!snapshot) {
                    const error = 'Snapshot not found for intent';
                    steps.push({
                        step: 'restore',
                        status: 'error',
                        durationMs: Date.now() - restoreStart,
                        error,
                    });
                    return this.buildErrorResult(replayId, intentId, '', '', steps, error, 'restore', options);
                }
                const restored = await this.snapshotManager.restore(snapshot.id, {
                    trustOverride: options.trustScoreOverride !== undefined || options.trustLevelOverride !== undefined
                        ? {
                            score: options.trustScoreOverride ?? snapshot.trust.score,
                            level: options.trustLevelOverride ?? snapshot.trust.level,
                        }
                        : undefined,
                });
                if (!restored) {
                    const error = 'Failed to restore snapshot';
                    steps.push({
                        step: 'restore',
                        status: 'error',
                        durationMs: Date.now() - restoreStart,
                        error,
                    });
                    return this.buildErrorResult(replayId, intentId, snapshot.id, snapshot.tenantId, steps, error, 'restore', options);
                }
                stepTiming.restore = Date.now() - restoreStart;
                steps.push({
                    step: 'restore',
                    status: 'completed',
                    durationMs: stepTiming.restore,
                    data: options.includeStepDetails ? { snapshotId: snapshot.id } : undefined,
                });
                if (options.stopAt === 'restore') {
                    return this.buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options);
                }
                // Apply speed factor delay
                if (options.speedFactor && options.speedFactor > 0 && options.speedFactor !== 1) {
                    // Simulate processing time based on speed factor
                    // speedFactor 2.0 = half the original time, 0.5 = double the time
                    await this.delay(100 / options.speedFactor);
                }
                // Step 2: Trust evaluation
                const trustStart = Date.now();
                const trust = options.trustScoreOverride !== undefined || options.trustLevelOverride !== undefined
                    ? {
                        ...restored.trust,
                        score: options.trustScoreOverride ?? restored.trust.score,
                        level: options.trustLevelOverride ?? restored.trust.level,
                    }
                    : restored.trust;
                stepTiming['trust-evaluation'] = Date.now() - trustStart;
                steps.push({
                    step: 'trust-evaluation',
                    status: 'completed',
                    durationMs: stepTiming['trust-evaluation'],
                    data: options.includeStepDetails ? { trustScore: trust.score, trustLevel: trust.level } : undefined,
                });
                if (options.stopAt === 'trust-evaluation') {
                    return this.buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options, trust);
                }
                // Step 3: Policy evaluation
                const policyStart = Date.now();
                let policies = restored.policies;
                // Apply policy modifications
                if (options.modifyPolicy) {
                    if (options.modifyPolicy.policies) {
                        policies = options.modifyPolicy.policies.map((p) => ({
                            id: p.id,
                            name: p.name,
                            namespace: p.namespace,
                            version: p.version,
                            checksum: p.checksum,
                            definition: p.definition,
                            capturedAt: new Date().toISOString(),
                        }));
                    }
                    if (options.modifyPolicy.additionalPolicies) {
                        const additional = options.modifyPolicy.additionalPolicies.map((p) => ({
                            id: p.id,
                            name: p.name,
                            namespace: p.namespace,
                            version: p.version,
                            checksum: p.checksum,
                            definition: p.definition,
                            capturedAt: new Date().toISOString(),
                        }));
                        policies = [...policies, ...additional];
                    }
                    if (options.modifyPolicy.excludePolicies) {
                        policies = policies.filter((p) => !options.modifyPolicy?.excludePolicies?.includes(p.id));
                    }
                }
                const evalResult = evaluatePolicies(restored.intent, policies, trust);
                stepTiming['policy-evaluation'] = Date.now() - policyStart;
                steps.push({
                    step: 'policy-evaluation',
                    status: 'completed',
                    durationMs: stepTiming['policy-evaluation'],
                    data: options.includeStepDetails ? {
                        policiesEvaluated: policies.length,
                        policiesApplied: evalResult.policiesApplied.length,
                    } : undefined,
                });
                if (options.stopAt === 'policy-evaluation') {
                    return this.buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options, trust, evalResult);
                }
                // Step 4: Decision
                const decisionStart = Date.now();
                stepTiming.decision = Date.now() - decisionStart;
                steps.push({
                    step: 'decision',
                    status: 'completed',
                    durationMs: stepTiming.decision,
                    data: options.includeStepDetails ? { action: evalResult.action, reason: evalResult.reason } : undefined,
                });
                if (options.stopAt === 'decision') {
                    return this.buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options, trust, evalResult);
                }
                // Step 5: Execution (if not dry run and action is allow)
                const executionStart = Date.now();
                if (!options.dryRun && evalResult.action === 'allow') {
                    // In a real implementation, this would trigger actual execution
                    stepTiming.execution = Date.now() - executionStart;
                    steps.push({
                        step: 'execution',
                        status: 'completed',
                        durationMs: stepTiming.execution,
                        data: options.includeStepDetails ? { executed: true } : undefined,
                    });
                }
                else {
                    steps.push({
                        step: 'execution',
                        status: 'skipped',
                        durationMs: 0,
                        data: options.includeStepDetails ? {
                            reason: options.dryRun ? 'dry run' : `action is ${evalResult.action}`,
                        } : undefined,
                    });
                }
                if (options.stopAt === 'execution') {
                    return this.buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options, trust, evalResult);
                }
                // Step 6: Complete
                stepTiming.complete = 0;
                steps.push({
                    step: 'complete',
                    status: 'completed',
                    durationMs: 0,
                });
                // Build differences (compare with original)
                const differences = [];
                if (snapshot.intent.trustScore !== trust.score) {
                    differences.push({
                        type: 'trust_score',
                        severity: 'info',
                        path: 'trust.score',
                        originalValue: snapshot.intent.trustScore,
                        replayValue: trust.score,
                        description: `Trust score changed from ${snapshot.intent.trustScore} to ${trust.score}`,
                    });
                }
                // Generate comparison report if requested
                let comparison;
                if (options.generateComparison) {
                    const originalExecution = {
                        intentId: snapshot.intentId,
                        action: (snapshot.intent.status === 'approved' ? 'allow' : snapshot.intent.status === 'denied' ? 'deny' : 'escalate'),
                        policiesApplied: [],
                        trustScore: snapshot.trust.score,
                        trustLevel: snapshot.trust.level,
                        durationMs: 0,
                        evaluatedAt: snapshot.capturedAt,
                    };
                    comparison = await this.comparator.compare(originalExecution, {
                        replayId,
                        intentId,
                        snapshotId: snapshot.id,
                        tenantId: snapshot.tenantId,
                        replayedAt: new Date().toISOString(),
                        success: true,
                        dryRun: options.dryRun ?? false,
                        steps,
                        outcome: {
                            action: evalResult.action,
                            reason: evalResult.reason,
                            trustScore: trust.score,
                            trustLevel: trust.level,
                            policiesApplied: evalResult.policiesApplied,
                        },
                        differences,
                        timing: {
                            totalDurationMs: Date.now() - startTime,
                            stepBreakdown: stepTiming,
                        },
                    });
                }
                const result = {
                    replayId,
                    intentId,
                    snapshotId: snapshot.id,
                    tenantId: snapshot.tenantId,
                    replayedAt: new Date().toISOString(),
                    success: true,
                    dryRun: options.dryRun ?? false,
                    steps,
                    outcome: {
                        action: evalResult.action,
                        reason: evalResult.reason,
                        trustScore: trust.score,
                        trustLevel: trust.level,
                        policiesApplied: evalResult.policiesApplied,
                    },
                    differences,
                    timing: {
                        totalDurationMs: Date.now() - startTime,
                        stepBreakdown: stepTiming,
                    },
                    comparison,
                };
                logger.info({
                    replayId,
                    intentId,
                    action: evalResult.action,
                    dryRun: options.dryRun,
                    durationMs: result.timing.totalDurationMs,
                }, 'Replay completed');
                span.setAttributes({
                    'replay.success': true,
                    'replay.action': evalResult.action,
                    'replay.duration_ms': result.timing.totalDurationMs,
                });
                span.setStatus({ code: SpanStatusCode.OK });
                return result;
            }
            catch (error) {
                const errorMessage = error instanceof Error ? error.message : 'Unknown error';
                span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage });
                if (error instanceof Error) {
                    span.recordException(error);
                }
                return this.buildErrorResult(replayId, intentId, '', '', steps, errorMessage, 'restore', options);
            }
            finally {
                span.end();
            }
        });
    }
    /**
     * Get the snapshot manager instance
     */
    getSnapshotManager() {
        return this.snapshotManager;
    }
    /**
     * Get the comparator instance
     */
    getComparator() {
        return this.comparator;
    }
    /**
     * Get the simulator instance
     */
    getSimulator() {
        return this.simulator;
    }
    /**
     * Capture a snapshot for an intent
     */
    async captureSnapshot(intent, trustData, policies, options) {
        return this.snapshotManager.capture(intent, trustData, policies, options);
    }
    /**
     * Helper to delay execution
     */
    delay(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    /**
     * Build error result
     */
    buildErrorResult(replayId, intentId, snapshotId, tenantId, steps, error, errorStep, options) {
        return {
            replayId,
            intentId,
            snapshotId,
            tenantId,
            replayedAt: new Date().toISOString(),
            success: false,
            dryRun: options.dryRun ?? false,
            steps,
            outcome: {
                action: 'deny',
                reason: 'Replay failed',
                trustScore: 0,
                trustLevel: 0,
            },
            differences: [],
            timing: {
                totalDurationMs: 0,
                stepBreakdown: {
                    restore: 0,
                    'trust-evaluation': 0,
                    'policy-evaluation': 0,
                    decision: 0,
                    execution: 0,
                    complete: 0,
                },
            },
            error: {
                message: error,
                step: errorStep,
            },
        };
    }
    /**
     * Build stopped result
     */
    buildStoppedResult(replayId, intentId, snapshot, restored, steps, stepTiming, options, trust, evalResult) {
        const totalDuration = Object.values(stepTiming).reduce((a, b) => a + b, 0);
        return {
            replayId,
            intentId,
            snapshotId: snapshot.id,
            tenantId: snapshot.tenantId,
            replayedAt: new Date().toISOString(),
            success: true,
            dryRun: options.dryRun ?? false,
            steps,
            outcome: {
                action: evalResult?.action ?? 'allow',
                reason: evalResult?.reason ?? 'Stopped before decision',
                trustScore: trust?.score ?? restored.trust.score,
                trustLevel: trust?.level ?? restored.trust.level,
                policiesApplied: evalResult?.policiesApplied,
            },
            differences: [],
            timing: {
                totalDurationMs: totalDuration,
                stepBreakdown: stepTiming,
            },
        };
    }
}
/**
 * Create a new ReplayEngine instance
 */
export function createReplayEngine() {
    return new ReplayEngine();
}
// Re-export types and classes from submodules
export { 
// Snapshot types
SnapshotManager, createSnapshotManager, } from './snapshot.js';
export { 
// Comparator types
ReplayComparator, createReplayComparator, } from './comparator.js';
export { 
// Simulator types
SimulationEngine, createSimulationEngine, } from './simulator.js';
//# sourceMappingURL=index.js.map