var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
import { getQuerySettings } from "../Utils/AnalyticsDatabase/QuerySettingsHelper";
import MetricService from "./MetricService";
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
import BadDataException from "../../Types/Exception/BadDataException";
import Includes from "../../Types/BaseDatabase/Includes";
import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import ServiceType from "../../Types/Telemetry/ServiceType";
/*
 * Facet aggregation for the Metrics page sidebar. Same shape as
 * TraceAggregationService / LogAggregationService — per-facet GROUP BY on
 * the analytics table, with a `primaryEntityType` discriminator that lets
 * the `primaryEntityId` column carry Host / DockerHost / KubernetesCluster
 * ids for the corresponding virtual facets.
 */
export class MetricAggregationService {
    static async getFacetValues(request) {
        const statement = MetricAggregationService.buildFacetStatement(request);
        const dbResult = await MetricService.executeQuery(statement);
        const response = await dbResult.json();
        const rows = response.data || [];
        return rows
            .map((row) => {
            return {
                value: String(row["val"] || ""),
                count: Number(row["cnt"] || 0),
            };
        })
            .filter((facet) => {
            return facet.value.length > 0;
        });
    }
    static buildFacetStatement(request) {
        var _a;
        // Pre-rename alias from stale clients; the V3 column is primaryEntityId.
        if (request.facetKey === "serviceId") {
            request.facetKey = "primaryEntityId";
        }
        const limit = (_a = request.limit) !== null && _a !== void 0 ? _a : MetricAggregationService.DEFAULT_FACET_LIMIT;
        MetricAggregationService.validateFacetKey(request.facetKey);
        const resourceServiceType = MetricAggregationService.RESOURCE_FACET_KEYS.get(request.facetKey);
        const isResourceFacet = resourceServiceType !== undefined;
        const isTopLevelColumn = isResourceFacet ||
            MetricAggregationService.isTopLevelColumn(request.facetKey);
        const statement = new Statement();
        if (isResourceFacet) {
            statement.append(SQL `SELECT toString(primaryEntityId) AS val, count() AS cnt FROM ${MetricAggregationService.TABLE_NAME}`);
        }
        else if (isTopLevelColumn) {
            statement.append(SQL `SELECT toString(${request.facetKey}) AS val, count() AS cnt FROM ${MetricAggregationService.TABLE_NAME}`);
        }
        else {
            statement.append(SQL `SELECT attributes[${{
                type: TableColumnType.Text,
                value: request.facetKey,
            }}] AS val, count() AS cnt FROM ${MetricAggregationService.TABLE_NAME}`);
        }
        statement.append(SQL ` WHERE projectId = ${{
            type: TableColumnType.ObjectID,
            value: request.projectId,
        }} AND time >= ${{
            type: TableColumnType.Date,
            value: request.startTime,
        }} AND time <= ${{
            type: TableColumnType.Date,
            value: request.endTime,
        }}`);
        if (isResourceFacet) {
            statement.append(SQL ` AND primaryEntityType = ${{
                type: TableColumnType.Text,
                value: resourceServiceType,
            }}`);
        }
        else if (request.facetKey === "primaryEntityId") {
            statement.append(SQL ` AND (primaryEntityType = '' OR primaryEntityType = ${{
                type: TableColumnType.Text,
                value: ServiceType.OpenTelemetry,
            }})`);
        }
        else if (!isTopLevelColumn) {
            statement.append(SQL ` AND mapContains(attributes, ${{
                type: TableColumnType.Text,
                value: request.facetKey,
            }})`);
        }
        /*
         * Read-side retention filter: rows past their per-service retention
         * stay in their part until the whole part drops (ttl_only_drop_parts).
         */
        statement.append(" AND retentionDate >= now()");
        MetricAggregationService.appendCommonFilters(statement, request);
        statement.append(SQL ` GROUP BY val ORDER BY cnt DESC LIMIT ${{
            type: TableColumnType.Number,
            value: limit,
        }}`);
        /*
         * Defense in depth: cap runtime below nginx's 60s proxy_read_timeout
         * so a slow facet never starves the endpoint.
         */
        statement.append(getQuerySettings({
            maxExecutionTimeInSeconds: 45,
            timeoutOverflowMode: "break",
        }));
        return statement;
    }
    static appendCommonFilters(statement, request) {
        if (request.serviceIds && request.serviceIds.length > 0) {
            statement.append(SQL ` AND primaryEntityId IN (${{
                type: TableColumnType.ObjectID,
                value: new Includes(request.serviceIds.map((id) => {
                    return id.toString();
                })),
            }})`);
        }
        if (request.metricNames && request.metricNames.length > 0) {
            statement.append(SQL ` AND name IN (${{
                type: TableColumnType.Text,
                value: new Includes(request.metricNames),
            }})`);
        }
    }
    static isTopLevelColumn(key) {
        return MetricAggregationService.TOP_LEVEL_COLUMNS.has(key);
    }
    static validateFacetKey(facetKey) {
        if (typeof facetKey !== "string") {
            throw new BadDataException("Invalid facetKey");
        }
        if (facetKey.length === 0 ||
            facetKey.length > MetricAggregationService.MAX_FACET_KEY_LENGTH) {
            throw new BadDataException("Invalid facetKey");
        }
        if (MetricAggregationService.isTopLevelColumn(facetKey) ||
            MetricAggregationService.RESOURCE_FACET_KEYS.has(facetKey)) {
            return;
        }
        if (!MetricAggregationService.ATTRIBUTE_KEY_PATTERN.test(facetKey)) {
            throw new BadDataException("Invalid facetKey");
        }
    }
}
MetricAggregationService.DEFAULT_FACET_LIMIT = 500;
MetricAggregationService.TABLE_NAME = AnalyticsTableName.Metric;
MetricAggregationService.TOP_LEVEL_COLUMNS = new Set([
    "primaryEntityId",
    "name",
]);
MetricAggregationService.RESOURCE_FACET_KEYS = new Map([
    ["hostId", ServiceType.Host],
    ["dockerHostId", ServiceType.DockerHost],
    ["kubernetesClusterId", ServiceType.KubernetesCluster],
    ["serverlessFunctionId", ServiceType.ServerlessFunction],
    ["cloudResourceId", ServiceType.CloudResource],
    ["rumApplicationId", ServiceType.RealUserMonitor],
]);
MetricAggregationService.ATTRIBUTE_KEY_PATTERN = /^[a-zA-Z0-9._:/-]+$/;
MetricAggregationService.MAX_FACET_KEY_LENGTH = 256;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], MetricAggregationService, "getFacetValues", null);
export default MetricAggregationService;
//# sourceMappingURL=MetricAggregationService.js.map