/**
 * BASIS Rule Evaluator
 *
 * Core evaluation engine for processing constraints against intents.
 * Uses the Vorion Expression Engine for evaluating condition expressions.
 */
import { createLogger } from '../common/logger.js';
import { ExpressionEngine, } from '../common/expression/index.js';
import { evaluateExpression } from './expression-evaluator.js';
const logger = createLogger({ component: 'basis-evaluator' });
/** Global expression engine instance */
const expressionEngine = new ExpressionEngine();
/**
 * Rule evaluator class
 */
export class RuleEvaluator {
    namespaces = new Map();
    compiledExpressions = new Map();
    /**
     * Register a rule namespace
     */
    registerNamespace(namespace) {
        this.namespaces.set(namespace.name, namespace);
        logger.info({ namespace: namespace.name }, 'Namespace registered');
    }
    /**
     * Unregister a rule namespace
     */
    unregisterNamespace(name) {
        this.namespaces.delete(name);
        logger.info({ namespace: name }, 'Namespace unregistered');
    }
    /**
     * Evaluate all applicable rules against the context
     */
    async evaluate(context) {
        const startTime = performance.now();
        const rulesEvaluated = [];
        const violatedRules = [];
        // Collect all applicable rules
        const applicableRules = this.getApplicableRules(context);
        // Sort by priority (lower number = higher priority)
        applicableRules.sort((a, b) => a.priority - b.priority);
        // Evaluate each rule
        for (const rule of applicableRules) {
            const result = await this.evaluateRule(rule, context);
            rulesEvaluated.push(result);
            if (!result.matched || result.action === 'deny') {
                violatedRules.push(result);
            }
            // Stop on first deny
            if (result.action === 'deny') {
                break;
            }
        }
        const totalDurationMs = performance.now() - startTime;
        // Determine final action
        const finalAction = this.determineFinalAction(rulesEvaluated);
        const passed = finalAction === 'allow';
        logger.info({
            intentId: context.intent.id,
            rulesEvaluated: rulesEvaluated.length,
            violations: violatedRules.length,
            finalAction,
            durationMs: totalDurationMs,
        }, 'Evaluation completed');
        return {
            passed,
            finalAction,
            rulesEvaluated,
            violatedRules,
            totalDurationMs,
            evaluatedAt: new Date().toISOString(),
        };
    }
    /**
     * Get rules that apply to the given context
     */
    getApplicableRules(context) {
        const applicable = [];
        for (const namespace of this.namespaces.values()) {
            for (const rule of namespace.rules) {
                if (!rule.enabled)
                    continue;
                if (this.ruleMatches(rule, context)) {
                    applicable.push(rule);
                }
            }
        }
        return applicable;
    }
    /**
     * Check if a rule's conditions match the context
     */
    ruleMatches(rule, context) {
        const { when } = rule;
        // Check intent type
        if (when.intentType) {
            const types = Array.isArray(when.intentType)
                ? when.intentType
                : [when.intentType];
            if (!types.includes(context.intent.type) && !types.includes('*')) {
                return false;
            }
        }
        // Check entity type
        if (when.entityType) {
            const types = Array.isArray(when.entityType)
                ? when.entityType
                : [when.entityType];
            if (!types.includes(context.entity.type) && !types.includes('*')) {
                return false;
            }
        }
        // Check additional conditions
        if (when.conditions) {
            for (const condition of when.conditions) {
                if (!this.evaluateCondition(condition, context)) {
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * Evaluate a single condition expression
     */
    evaluateCondition(condition, context) {
        const fieldValue = this.resolveField(condition.field, context);
        const targetValue = condition.value;
        switch (condition.operator) {
            case 'equals':
                return fieldValue === targetValue;
            case 'not_equals':
                return fieldValue !== targetValue;
            case 'greater_than':
                return fieldValue > targetValue;
            case 'less_than':
                return fieldValue < targetValue;
            case 'greater_than_or_equal':
                return fieldValue >= targetValue;
            case 'less_than_or_equal':
                return fieldValue <= targetValue;
            case 'in':
                return targetValue.includes(fieldValue);
            case 'not_in':
                return !targetValue.includes(fieldValue);
            case 'contains':
                return String(fieldValue).includes(String(targetValue));
            case 'not_contains':
                return !String(fieldValue).includes(String(targetValue));
            case 'exists':
                return fieldValue !== undefined && fieldValue !== null;
            case 'not_exists':
                return fieldValue === undefined || fieldValue === null;
            default:
                logger.warn({ operator: condition.operator }, 'Unknown operator');
                return false;
        }
    }
    /**
     * Resolve a field path to its value in the context
     */
    resolveField(field, context) {
        const parts = field.split('.');
        let current = context;
        for (const part of parts) {
            if (current === null || current === undefined) {
                return undefined;
            }
            current = current[part];
        }
        return current;
    }
    /**
     * Evaluate a single rule against the context
     */
    async evaluateRule(rule, context) {
        const startTime = performance.now();
        let action = 'allow';
        let reason = 'No conditions matched';
        let evaluationError;
        // Evaluate each evaluation step using the Expression Engine
        for (const evaluation of rule.evaluate) {
            try {
                const conditionMet = this.evaluateExpressionSafe(evaluation.condition, context);
                if (conditionMet) {
                    action = evaluation.result;
                    reason = evaluation.reason ?? `Condition matched: ${evaluation.condition}`;
                    break;
                }
            }
            catch (error) {
                // Log the error but continue to next evaluation
                const errorMessage = error instanceof Error ? error.message : String(error);
                logger.warn({
                    ruleId: rule.id,
                    condition: evaluation.condition,
                    error: errorMessage,
                }, 'Expression evaluation failed');
                evaluationError = errorMessage;
                // On evaluation error, treat as condition not met and continue
            }
        }
        const durationMs = performance.now() - startTime;
        return {
            ruleId: rule.id,
            ruleName: rule.name,
            matched: action !== 'deny',
            action,
            reason,
            details: {
                priority: rule.priority,
                evaluationsChecked: rule.evaluate.length,
                ...(evaluationError && { evaluationError }),
            },
            durationMs,
        };
    }
    /**
     * Safely evaluate a condition expression string
     *
     * Uses the secure expression evaluator that supports:
     * - Logical operators: AND, OR, NOT (or &&, ||, !)
     * - Comparison operators: ==, !=, <, >, <=, >=
     * - String operations: contains(), startsWith(), endsWith(), matches()
     * - Arithmetic: +, -, *, /, %
     * - Variable interpolation from context (e.g., entity.trustLevel, intent.goal)
     * - Nested expressions with parentheses
     *
     * @param expression - The expression string to evaluate
     * @param context - The evaluation context
     * @returns boolean result of the expression
     * @throws ExpressionError on invalid expressions
     */
    evaluateExpressionSafe(expression, context) {
        return evaluateExpression(expression, context, {
            maxDepth: 10,
            maxLength: 1000,
            timeoutMs: 100,
        });
    }
    /**
     * Determine the final action from all rule results
     */
    determineFinalAction(results) {
        // Priority: deny > escalate > limit > monitor > allow
        const priorities = {
            deny: 0,
            terminate: 1,
            escalate: 2,
            limit: 3,
            constrain: 4,
            monitor: 5,
            allow: 6,
        };
        let finalAction = 'allow';
        let lowestPriority = priorities['allow'];
        for (const result of results) {
            const priority = priorities[result.action];
            if (priority < lowestPriority) {
                finalAction = result.action;
                lowestPriority = priority;
            }
        }
        return finalAction;
    }
}
/**
 * Create a new rule evaluator instance
 */
export function createEvaluator() {
    return new RuleEvaluator();
}
//# sourceMappingURL=evaluator.js.map