/**
 * Governance Engine
 *
 * Core engine for evaluating intents against policies and making governance decisions.
 *
 * @packageDocumentation
 */
import { v4 as uuidv4 } from 'uuid';
import { createLogger } from '../common/logger.js';
import { withSpan } from '../common/trace.js';
import { resolveConflicts, PolicySetManager, } from './policy.js';
import { RuleEvaluator, } from './evaluator.js';
const logger = createLogger({ component: 'governance-engine' });
// =============================================================================
// GOVERNANCE ENGINE
// =============================================================================
/**
 * GovernanceEngine class for evaluating intents against policies
 */
export class GovernanceEngine {
    ruleEvaluator;
    policySetManager;
    options;
    constructor(options = {}) {
        this.ruleEvaluator = new RuleEvaluator();
        this.policySetManager = new PolicySetManager();
        this.options = {
            conflictResolution: options.conflictResolution ?? 'deny-overrides',
            defaultDecision: options.defaultDecision ?? 'allow',
            enableTracing: options.enableTracing ?? true,
            proofPlane: options.proofPlane,
        };
        logger.info({
            conflictResolution: this.options.conflictResolution,
            defaultDecision: this.options.defaultDecision,
        }, 'Governance engine initialized');
    }
    /**
     * Evaluate an intent against a set of policies
     */
    evaluateIntent(intent, policies) {
        const startTime = performance.now();
        const context = {
            intent,
            context: intent.context,
        };
        const policiesEvaluated = [];
        const matchedPolicies = [];
        const matchedEffects = [];
        // Sort policies by priority (lower = higher priority)
        const sortedPolicies = [...policies].sort((a, b) => a.priority - b.priority);
        for (const policy of sortedPolicies) {
            const result = this.ruleEvaluator.evaluatePolicy(policy, context);
            const auditEntry = this.createPolicyAuditEntry(result);
            policiesEvaluated.push(auditEntry);
            if (result.matched) {
                matchedPolicies.push(auditEntry);
                matchedEffects.push({
                    effect: result.effect,
                    priority: policy.priority,
                });
                logger.debug({
                    policyId: policy.id,
                    policyName: policy.name,
                    effect: result.effect,
                    intentId: intent.id,
                }, 'Policy matched');
                // Short-circuit on deny if using deny-overrides
                if (this.options.conflictResolution === 'deny-overrides' && result.effect === 'deny') {
                    logger.info({
                        policyId: policy.id,
                        policyName: policy.name,
                        intentId: intent.id,
                    }, 'Deny policy matched - short-circuiting');
                    break;
                }
            }
        }
        // Determine final decision
        let decision;
        let reason;
        if (matchedEffects.length === 0) {
            // No policies matched, use default
            decision = this.options.defaultDecision;
            reason = 'No matching policies found';
        }
        else {
            const finalEffect = resolveConflicts(matchedEffects, this.options.conflictResolution);
            decision = finalEffect === 'deny' ? 'deny' : 'allow';
            reason = `Resolved by ${this.options.conflictResolution} strategy`;
        }
        const durationMs = performance.now() - startTime;
        logger.info({
            intentId: intent.id,
            decision,
            policiesEvaluated: policiesEvaluated.length,
            policiesMatched: matchedPolicies.length,
            durationMs,
        }, 'Intent evaluation completed');
        const result = {
            intentId: intent.id,
            decision,
            reason,
            permitted: decision === 'allow',
            policiesEvaluated,
            matchedPolicies,
            durationMs,
            evaluatedAt: new Date().toISOString(),
        };
        // Fire-and-forget proof-plane emission
        if (this.options.proofPlane) {
            this.emitDecisionProof(result, intent).catch((err) => {
                logger.warn({ err, intentId: intent.id }, 'Failed to emit decision proof');
            });
        }
        return result;
    }
    /**
     * Evaluate an intent with OpenTelemetry tracing
     */
    async evaluateIntentWithTracing(intent, policies) {
        if (!this.options.enableTracing) {
            return this.evaluateIntent(intent, policies);
        }
        return withSpan('governance.evaluateIntent', async (span) => {
            span.attributes['intent.id'] = intent.id;
            span.attributes['intent.goal'] = intent.goal;
            span.attributes['intent.type'] = intent.intentType ?? 'unknown';
            span.attributes['policies.count'] = policies.length;
            const result = this.evaluateIntent(intent, policies);
            span.attributes['result.decision'] = result.decision;
            span.attributes['result.permitted'] = result.permitted;
            span.attributes['result.policiesMatched'] = result.matchedPolicies.length;
            span.attributes['result.durationMs'] = result.durationMs;
            return result;
        }, { 'tenant.id': intent.tenantId });
    }
    /**
     * Load policies from policy sets
     */
    loadPoliciesFromSets() {
        return this.policySetManager.getAllEnabledPolicies();
    }
    /**
     * Add a policy set to the engine
     */
    addPolicySet(policySet) {
        this.policySetManager.addPolicySet(policySet);
        logger.info({
            policySetId: policySet.id,
            policySetName: policySet.name,
            policiesCount: policySet.policies.length,
        }, 'Policy set added');
    }
    /**
     * Remove a policy set from the engine
     */
    removePolicySet(id) {
        const removed = this.policySetManager.removePolicySet(id);
        if (removed) {
            logger.info({ policySetId: id }, 'Policy set removed');
        }
        return removed;
    }
    /**
     * Get all policy sets
     */
    getPolicySets() {
        return this.policySetManager.getAllPolicySets();
    }
    /**
     * Find policies matching specific criteria
     */
    findMatchingPolicies(action, resource, intentType) {
        return this.policySetManager.findMatchingPolicies(action, resource, intentType);
    }
    /**
     * Clear all loaded policies
     */
    clearPolicies() {
        this.policySetManager.clear();
        logger.info('All policy sets cleared');
    }
    /**
     * Get engine options
     */
    getOptions() {
        return { ...this.options };
    }
    /**
     * Create an audit entry from a policy match result
     */
    createPolicyAuditEntry(result) {
        const ruleAudit = result.ruleGroupResult.ruleResults.map((ruleResult) => ({
            policyId: result.policy.id,
            policyName: result.policy.name,
            field: ruleResult.rule.field,
            operator: ruleResult.rule.operator,
            expectedValue: ruleResult.expectedValue,
            actualValue: ruleResult.actualValue,
            matched: ruleResult.matched,
        }));
        return {
            policyId: result.policy.id,
            policyName: result.policy.name,
            priority: result.policy.priority,
            matched: result.matched,
            effect: result.effect,
            ruleAudit,
            durationMs: result.durationMs,
        };
    }
    /**
     * Emit a decision proof event to the proof plane (fire-and-forget)
     */
    async emitDecisionProof(result, intent) {
        const correlationId = intent.correlationId ?? uuidv4();
        const now = new Date();
        const expiresAt = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24h TTL
        const decision = {
            decisionId: uuidv4(),
            intentId: result.intentId,
            agentId: intent.entityId,
            correlationId,
            permitted: result.permitted,
            trustBand: intent.trustLevel ?? 4, // default T4_STANDARD
            trustScore: intent.trustScore ?? 0,
            reasoning: result.reason ? [result.reason] : [],
            decidedAt: now,
            expiresAt,
            latencyMs: result.durationMs,
            version: 1,
        };
        await this.options.proofPlane.logDecisionMade(decision, correlationId);
        logger.debug({
            decisionId: decision.decisionId,
            intentId: result.intentId,
            correlationId,
        }, 'Decision proof emitted');
    }
}
/**
 * Create a new governance engine instance
 */
export function createGovernanceEngine(options) {
    return new GovernanceEngine(options);
}
//# sourceMappingURL=engine.js.map