import TraceAggregationType from "./TraceAggregationType";
// Maximum number of sources per rule — bounds per-cron workload.
export const TRACE_RECORDING_RULE_MAX_SOURCES = 4;
// Maximum expression length — prevents pathological parser input.
export const TRACE_RECORDING_RULE_MAX_EXPRESSION_LENGTH = 500;
/*
 * Alphabet used to generate source aliases. v1 caps at TRACE_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 TraceRecordingRuleDefinitionUtil {
    static getAggregationOptions() {
        return [
            {
                value: TraceAggregationType.Count,
                label: "Count",
                description: "Number of matching spans.",
            },
            {
                value: TraceAggregationType.ErrorCount,
                label: "Error Count",
                description: "Spans with status = error.",
            },
            {
                value: TraceAggregationType.AvgDurationSeconds,
                label: "Avg Duration (s)",
                description: "Average span duration in seconds.",
            },
            {
                value: TraceAggregationType.P50DurationSeconds,
                label: "p50 Duration (s)",
                description: "Median span duration.",
            },
            {
                value: TraceAggregationType.P90DurationSeconds,
                label: "p90 Duration (s)",
                description: "90th percentile span duration.",
            },
            {
                value: TraceAggregationType.P95DurationSeconds,
                label: "p95 Duration (s)",
                description: "95th percentile span duration.",
            },
            {
                value: TraceAggregationType.P99DurationSeconds,
                label: "p99 Duration (s)",
                description: "99th percentile span duration.",
            },
            {
                value: TraceAggregationType.MaxDurationSeconds,
                label: "Max Duration (s)",
                description: "Longest span in the bucket.",
            },
            {
                value: TraceAggregationType.MinDurationSeconds,
                label: "Min Duration (s)",
                description: "Shortest span in the bucket.",
            },
        ];
    }
    static getSpanKindOptions() {
        return [
            { value: "", label: "Any" },
            { value: "SPAN_KIND_SERVER", label: "Server" },
            { value: "SPAN_KIND_CLIENT", label: "Client" },
            { value: "SPAN_KIND_PRODUCER", label: "Producer" },
            { value: "SPAN_KIND_CONSUMER", label: "Consumer" },
            { value: "SPAN_KIND_INTERNAL", label: "Internal" },
        ];
    }
    /*
     * Combined attribute filters for a source: the multi-filter array plus the
     * legacy single pair (deduplicated by key, array entries win).
     */
    static getSourceAttributeFilters(source) {
        const filters = [];
        const seenKeys = new Set();
        for (const filter of source.filterAttributes || []) {
            if (!filter || typeof filter !== "object") {
                continue;
            }
            const key = (filter.key || "").trim();
            const value = (filter.value || "").trim();
            if (!key || !value || seenKeys.has(key)) {
                continue;
            }
            seenKeys.add(key);
            filters.push({ key, value });
        }
        const legacyKey = (source.filterAttributeKey || "").trim();
        const legacyValue = (source.filterAttributeValue || "").trim();
        if (legacyKey && legacyValue && !seenKeys.has(legacyKey)) {
            filters.push({ key: legacyKey, value: legacyValue });
        }
        return filters;
    }
    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",
                    aggregationType: TraceAggregationType.Count,
                },
            ],
            expression: "A",
            groupByAttribute: "",
        };
    }
    static getValidationError(definition) {
        var _a, _b, _c, _d;
        if (!definition) {
            return "Definition is required.";
        }
        const sources = definition.sources || [];
        if (sources.length === 0) {
            return "Add at least one source.";
        }
        if (sources.length > TRACE_RECORDING_RULE_MAX_SOURCES) {
            return `A rule can reference at most ${TRACE_RECORDING_RULE_MAX_SOURCES} sources.`;
        }
        const aliases = new Set();
        for (let i = 0; i < sources.length; i++) {
            const source = sources[i];
            // Definitions arrive as JSON (deep links, API writes) — never throw.
            if (!source || typeof source !== "object") {
                return `Source #${i + 1}: Invalid source.`;
            }
            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 (!source.aggregationType) {
                return `${prefix}Aggregation type is required.`;
            }
            const hasFilterKey = Boolean((_a = source.filterAttributeKey) === null || _a === void 0 ? void 0 : _a.trim());
            const hasFilterValue = Boolean((_b = source.filterAttributeValue) === null || _b === void 0 ? void 0 : _b.trim());
            if (hasFilterKey !== hasFilterValue) {
                return `${prefix}Attribute filter needs both a key and a value (or leave both empty).`;
            }
            if (source.filterAttributes !== undefined &&
                !Array.isArray(source.filterAttributes)) {
                return `${prefix}Invalid attribute filters.`;
            }
            for (const filter of source.filterAttributes || []) {
                if (!filter || typeof filter !== "object") {
                    return `${prefix}Invalid attribute filter row.`;
                }
                const hasKey = Boolean((_c = filter.key) === null || _c === void 0 ? void 0 : _c.trim());
                const hasValue = Boolean((_d = filter.value) === null || _d === void 0 ? void 0 : _d.trim());
                if (hasKey !== hasValue) {
                    return `${prefix}Attribute filter needs both a key and a value (or remove the row).`;
                }
            }
        }
        const expression = (definition.expression || "").trim();
        if (!expression) {
            return "Expression is required.";
        }
        if (expression.length > TRACE_RECORDING_RULE_MAX_EXPRESSION_LENGTH) {
            return `Expression must be ${TRACE_RECORDING_RULE_MAX_EXPRESSION_LENGTH} characters or fewer.`;
        }
        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.`;
            }
        }
        if (referencedAliases.size === 0) {
            return "Expression must reference at least one source alias (e.g. A, B).";
        }
        if (!EXPRESSION_REGEX.test(expression)) {
            return "Expression may only contain aliases (A-Z), numbers, operators (+ - * /), parentheses, and spaces.";
        }
        return null;
    }
}
//# sourceMappingURL=TraceRecordingRuleDefinition.js.map