import AggregationType from "../BaseDatabase/AggregationType";
/*
 * Maximum number of source metrics per rule for v1. Kept small to bound the
 * per-cron workload.
 */
export const RECORDING_RULE_MAX_SOURCES = 4;
// Maximum expression length for v1. Prevents pathological parser input.
export const RECORDING_RULE_MAX_EXPRESSION_LENGTH = 500;
/*
 * Alphabet used to generate source aliases. v1 caps at RECORDING_RULE_MAX_SOURCES
 * so we never exceed the first few letters in practice.
 */
const ALIAS_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const ALIAS_REGEX = /^[A-Z]$/;
const EXPRESSION_REGEX = /^[A-Z0-9+\-*/().\s]+$/;
export class RecordingRuleDefinitionUtil {
    static getAggregationOptions() {
        return [
            { value: AggregationType.Sum, label: "Sum" },
            { value: AggregationType.Avg, label: "Average" },
            { value: AggregationType.Count, label: "Count" },
            { value: AggregationType.Min, label: "Minimum" },
            { value: AggregationType.Max, label: "Maximum" },
        ];
    }
    static getNextAlias(sources) {
        const used = new Set((sources || []).map((s) => {
            return s.alias;
        }));
        for (const letter of ALIAS_ALPHABET) {
            if (!used.has(letter)) {
                return letter;
            }
        }
        return "A";
    }
    static getEmptyDefinition() {
        return {
            sources: [
                {
                    alias: "A",
                    metricName: "",
                    aggregationType: AggregationType.Sum,
                },
            ],
            expression: "A",
            groupByAttribute: "",
        };
    }
    static getValidationError(definition) {
        var _a, _b, _c;
        if (!definition) {
            return "Definition is required.";
        }
        const sources = definition.sources || [];
        if (sources.length === 0) {
            return "Add at least one source metric.";
        }
        if (sources.length > RECORDING_RULE_MAX_SOURCES) {
            return `A rule can reference at most ${RECORDING_RULE_MAX_SOURCES} source metrics.`;
        }
        const aliases = new Set();
        for (let i = 0; i < sources.length; i++) {
            const source = sources[i];
            const prefix = `Source ${source.alias || `#${i + 1}`}: `;
            if (!source.alias || !ALIAS_REGEX.test(source.alias)) {
                return `${prefix}Alias must be a single uppercase letter A-Z.`;
            }
            if (aliases.has(source.alias)) {
                return `${prefix}Duplicate alias. Each source must have a unique letter.`;
            }
            aliases.add(source.alias);
            if (!((_a = source.metricName) === null || _a === void 0 ? void 0 : _a.trim())) {
                return `${prefix}Metric name is required.`;
            }
            if (!source.aggregationType) {
                return `${prefix}Aggregation type is required.`;
            }
            const hasFilterKey = Boolean((_b = source.filterAttributeKey) === null || _b === void 0 ? void 0 : _b.trim());
            const hasFilterValue = Boolean((_c = source.filterAttributeValue) === null || _c === void 0 ? void 0 : _c.trim());
            if (hasFilterKey !== hasFilterValue) {
                return `${prefix}Attribute filter needs both a key and a value (or leave both empty).`;
            }
        }
        const expression = (definition.expression || "").trim();
        if (!expression) {
            return "Expression is required.";
        }
        if (expression.length > RECORDING_RULE_MAX_EXPRESSION_LENGTH) {
            return `Expression must be ${RECORDING_RULE_MAX_EXPRESSION_LENGTH} characters or fewer.`;
        }
        // Expression may only use aliases defined in sources.
        const referencedAliases = new Set(expression.match(/[A-Z]/g) || []);
        for (const alias of referencedAliases) {
            if (!aliases.has(alias)) {
                return `Expression references alias '${alias}' which is not defined in sources.`;
            }
        }
        // Must reference at least one alias — otherwise the expression is a constant.
        if (referencedAliases.size === 0) {
            return "Expression must reference at least one source alias (e.g. A, B).";
        }
        // Reject any character outside the allowed DSL grammar.
        if (!EXPRESSION_REGEX.test(expression)) {
            return "Expression may only contain aliases (A-Z), numbers, operators (+ - * /), parentheses, and spaces.";
        }
        return null;
    }
}
//# sourceMappingURL=RecordingRuleDefinition.js.map