"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.friendlyName = exports.capitalize = exports.defaultSql = exports.isFilterableItem = exports.isFilterableField = exports.isFilterableDimension = exports.isCompiledMetric = exports.isPostCalculationMetric = exports.isPostCalculationMetricType = exports.isNonAggregateMetricType = exports.isAggregateMetricType = exports.AggregateMetricTypes = exports.isNonAggregateMetric = exports.isMetric = exports.PostCalculationMetricTypes = exports.parseMetricType = exports.isFormat = exports.Format = exports.MetricType = exports.getFieldLabel = exports.getFieldRef = exports.isTimeBasedDimension = exports.isDimension = exports.hasFieldCompilationError = exports.DimensionType = exports.convertFieldRefToFieldId = exports.isField = exports.isFormulaTableCalculation = exports.isTemplateTableCalculation = exports.isSqlTableCalculation = exports.isTableCalculation = exports.FieldType = exports.TableCalculationTemplateType = exports.FrameBoundaryType = exports.FrameType = exports.unaryWindowFunctions = exports.nullaryWindowFunctions = exports.WindowFunctionType = exports.TableCalculationType = exports.CustomFormatType = exports.isCompiledCustomSqlDimension = exports.isCustomSqlDimension = exports.isCustomBinDimension = exports.isCustomDimension = exports.CustomDimensionType = exports.GroupValueMatchType = exports.BinType = exports.CompactConfigMap = exports.NumberSeparator = exports.Compact = void 0;
exports.IECByteCompacts = exports.isSummable = void 0;
exports.findCompactConfig = findCompactConfig;
exports.getCompactOptionsForFormatType = getCompactOptionsForFormatType;
const tslib_1 = require("tslib");
const words_1 = tslib_1.__importDefault(require("lodash/words"));
const errors_1 = require("./errors");
var Compact;
(function (Compact) {
    Compact["THOUSANDS"] = "thousands";
    Compact["MILLIONS"] = "millions";
    Compact["BILLIONS"] = "billions";
    Compact["TRILLIONS"] = "trillions";
    Compact["KILOBYTES"] = "kilobytes";
    Compact["MEGABYTES"] = "megabytes";
    Compact["GIGABYTES"] = "gigabytes";
    Compact["TERABYTES"] = "terabytes";
    Compact["PETABYTES"] = "petabytes";
    Compact["KIBIBYTES"] = "kibibytes";
    Compact["MEBIBYTES"] = "mebibytes";
    Compact["GIBIBYTES"] = "gibibytes";
    Compact["TEBIBYTES"] = "tebibytes";
    Compact["PEBIBYTES"] = "pebibytes";
})(Compact || (exports.Compact = Compact = {}));
const CompactAlias = [
    'K',
    'thousand',
    'M',
    'million',
    'B',
    'billion',
    'T',
    'trillion',
    'KB',
    'kilobyte',
    'MB',
    'megabyte',
    'GB',
    'gigabyte',
    'TB',
    'terabyte',
    'PB',
    'petabyte',
    'KiB',
    'kibibyte',
    'MiB',
    'mebibyte',
    'GiB',
    'gibibyte',
    'TiB',
    'tebibyte',
    'PiB',
    'pebibyte',
];
var NumberSeparator;
(function (NumberSeparator) {
    NumberSeparator["DEFAULT"] = "default";
    NumberSeparator["COMMA_PERIOD"] = "commaPeriod";
    NumberSeparator["SPACE_PERIOD"] = "spacePeriod";
    NumberSeparator["PERIOD_COMMA"] = "periodComma";
    NumberSeparator["NO_SEPARATOR_PERIOD"] = "noSeparatorPeriod";
})(NumberSeparator || (exports.NumberSeparator = NumberSeparator = {}));
exports.CompactConfigMap = {
    [Compact.THOUSANDS]: {
        compact: Compact.THOUSANDS,
        alias: ['K', 'thousand'],
        orderOfMagnitude: 3,
        convertFn: (value) => value / 1000,
        label: 'thousands (K)',
        suffix: 'K',
    },
    [Compact.MILLIONS]: {
        compact: Compact.MILLIONS,
        alias: ['M', 'million'],
        orderOfMagnitude: 6,
        convertFn: (value) => value / 1000000,
        label: 'millions (M)',
        suffix: 'M',
    },
    [Compact.BILLIONS]: {
        compact: Compact.BILLIONS,
        alias: ['B', 'billion'],
        orderOfMagnitude: 9,
        convertFn: (value) => value / 1000000000,
        label: 'billions (B)',
        suffix: 'B',
    },
    [Compact.TRILLIONS]: {
        compact: Compact.TRILLIONS,
        alias: ['T', 'trillion'],
        orderOfMagnitude: 12,
        convertFn: (value) => value / 1000000000000,
        label: 'trillions (T)',
        suffix: 'T',
    },
    [Compact.KILOBYTES]: {
        compact: Compact.KILOBYTES,
        alias: ['KB', 'kilobyte'],
        orderOfMagnitude: 3,
        convertFn: (value) => value / 1000,
        label: 'kilobytes (KB)',
        suffix: 'KB',
    },
    [Compact.MEGABYTES]: {
        compact: Compact.MEGABYTES,
        alias: ['MB', 'megabyte'],
        orderOfMagnitude: 6,
        convertFn: (value) => value / 1000000,
        label: 'megabytes (MB)',
        suffix: 'MB',
    },
    [Compact.GIGABYTES]: {
        compact: Compact.GIGABYTES,
        alias: ['GB', 'gigabyte'],
        orderOfMagnitude: 9,
        convertFn: (value) => value / 1000000000,
        label: 'gigabytes (GB)',
        suffix: 'GB',
    },
    [Compact.TERABYTES]: {
        compact: Compact.TERABYTES,
        alias: ['TB', 'terabyte'],
        orderOfMagnitude: 12,
        convertFn: (value) => value / 1000000000000,
        label: 'terabytes (TB)',
        suffix: 'TB',
    },
    [Compact.PETABYTES]: {
        compact: Compact.PETABYTES,
        alias: ['PB', 'petabyte'],
        orderOfMagnitude: 15,
        convertFn: (value) => value / 1000000000000000,
        label: 'petabytes (PB)',
        suffix: 'PB',
    },
    [Compact.KIBIBYTES]: {
        compact: Compact.KIBIBYTES,
        alias: ['KiB', 'kibibyte'],
        orderOfMagnitude: -1,
        convertFn: (value) => value / 1024,
        label: 'kibibytes (KiB)',
        suffix: 'KiB',
    },
    [Compact.MEBIBYTES]: {
        compact: Compact.MEBIBYTES,
        alias: ['MiB', 'mebibyte'],
        orderOfMagnitude: -1,
        convertFn: (value) => value / 1048576,
        label: 'mebibytes (MiB)',
        suffix: 'MiB',
    },
    [Compact.GIBIBYTES]: {
        compact: Compact.GIBIBYTES,
        alias: ['GiB', 'gibibyte'],
        orderOfMagnitude: -1,
        convertFn: (value) => value / 1073741824,
        label: 'gibibytes (GiB)',
        suffix: 'GiB',
    },
    [Compact.TEBIBYTES]: {
        compact: Compact.TEBIBYTES,
        alias: ['TiB', 'tebibyte'],
        orderOfMagnitude: -1,
        convertFn: (value) => value / 1099511627776,
        label: 'tebibytes (TiB)',
        suffix: 'TiB',
    },
    [Compact.PEBIBYTES]: {
        compact: Compact.PEBIBYTES,
        alias: ['PiB', 'pebibyte'],
        orderOfMagnitude: -1,
        convertFn: (value) => value / 1125899906842624,
        label: 'pebibytes (PiB)',
        suffix: 'PiB',
    },
};
function findCompactConfig(compactOrAlias) {
    return Object.values(exports.CompactConfigMap).find(({ compact, alias }) => compact === compactOrAlias ||
        alias.includes(compactOrAlias));
}
var BinType;
(function (BinType) {
    BinType["FIXED_NUMBER"] = "fixed_number";
    BinType["FIXED_WIDTH"] = "fixed_width";
    BinType["CUSTOM_RANGE"] = "custom_range";
    BinType["CUSTOM_GROUP"] = "custom_group";
})(BinType || (exports.BinType = BinType = {}));
var GroupValueMatchType;
(function (GroupValueMatchType) {
    GroupValueMatchType["EXACT"] = "exact";
    GroupValueMatchType["STARTS_WITH"] = "startsWith";
    GroupValueMatchType["ENDS_WITH"] = "endsWith";
    GroupValueMatchType["INCLUDES"] = "includes";
})(GroupValueMatchType || (exports.GroupValueMatchType = GroupValueMatchType = {}));
var CustomDimensionType;
(function (CustomDimensionType) {
    CustomDimensionType["BIN"] = "bin";
    CustomDimensionType["SQL"] = "sql";
})(CustomDimensionType || (exports.CustomDimensionType = CustomDimensionType = {}));
const isCustomDimension = (value) => value !== undefined &&
    Object.values(CustomDimensionType).includes(value.type);
exports.isCustomDimension = isCustomDimension;
const isCustomBinDimension = (value) => value !== undefined &&
    (0, exports.isCustomDimension)(value) &&
    value.type === CustomDimensionType.BIN;
exports.isCustomBinDimension = isCustomBinDimension;
const isCustomSqlDimension = (value) => value !== undefined &&
    (0, exports.isCustomDimension)(value) &&
    value.type === CustomDimensionType.SQL;
exports.isCustomSqlDimension = isCustomSqlDimension;
const isCompiledCustomSqlDimension = (value) => (0, exports.isCustomSqlDimension)(value) && 'compiledSql' in value;
exports.isCompiledCustomSqlDimension = isCompiledCustomSqlDimension;
var CustomFormatType;
(function (CustomFormatType) {
    CustomFormatType["DEFAULT"] = "default";
    CustomFormatType["PERCENT"] = "percent";
    CustomFormatType["CURRENCY"] = "currency";
    CustomFormatType["NUMBER"] = "number";
    CustomFormatType["ID"] = "id";
    CustomFormatType["DATE"] = "date";
    CustomFormatType["TIMESTAMP"] = "timestamp";
    CustomFormatType["BYTES_SI"] = "bytes_si";
    CustomFormatType["BYTES_IEC"] = "bytes_iec";
    CustomFormatType["CUSTOM"] = "custom";
})(CustomFormatType || (exports.CustomFormatType = CustomFormatType = {}));
var TableCalculationType;
(function (TableCalculationType) {
    TableCalculationType["NUMBER"] = "number";
    TableCalculationType["STRING"] = "string";
    TableCalculationType["DATE"] = "date";
    TableCalculationType["TIMESTAMP"] = "timestamp";
    TableCalculationType["BOOLEAN"] = "boolean";
})(TableCalculationType || (exports.TableCalculationType = TableCalculationType = {}));
var WindowFunctionType;
(function (WindowFunctionType) {
    WindowFunctionType["ROW_NUMBER"] = "row_number";
    WindowFunctionType["PERCENT_RANK"] = "percent_rank";
    WindowFunctionType["CUME_DIST"] = "cume_dist";
    WindowFunctionType["RANK"] = "rank";
    WindowFunctionType["SUM"] = "sum";
    WindowFunctionType["AVG"] = "avg";
    WindowFunctionType["COUNT"] = "count";
    WindowFunctionType["MIN"] = "min";
    WindowFunctionType["MAX"] = "max";
})(WindowFunctionType || (exports.WindowFunctionType = WindowFunctionType = {}));
exports.nullaryWindowFunctions = [
    WindowFunctionType.ROW_NUMBER,
    WindowFunctionType.PERCENT_RANK,
    WindowFunctionType.CUME_DIST,
    WindowFunctionType.RANK,
];
exports.unaryWindowFunctions = [
    WindowFunctionType.SUM,
    WindowFunctionType.AVG,
    WindowFunctionType.COUNT,
    WindowFunctionType.MIN,
    WindowFunctionType.MAX,
];
var FrameType;
(function (FrameType) {
    FrameType["ROWS"] = "rows";
    FrameType["RANGE"] = "range";
})(FrameType || (exports.FrameType = FrameType = {}));
var FrameBoundaryType;
(function (FrameBoundaryType) {
    FrameBoundaryType["UNBOUNDED_PRECEDING"] = "unbounded_preceding";
    FrameBoundaryType["PRECEDING"] = "preceding";
    FrameBoundaryType["CURRENT_ROW"] = "current_row";
    FrameBoundaryType["FOLLOWING"] = "following";
    FrameBoundaryType["UNBOUNDED_FOLLOWING"] = "unbounded_following";
})(FrameBoundaryType || (exports.FrameBoundaryType = FrameBoundaryType = {}));
var TableCalculationTemplateType;
(function (TableCalculationTemplateType) {
    TableCalculationTemplateType["PERCENT_CHANGE_FROM_PREVIOUS"] = "percent_change_from_previous";
    TableCalculationTemplateType["PERCENT_OF_PREVIOUS_VALUE"] = "percent_of_previous_value";
    TableCalculationTemplateType["PERCENT_OF_COLUMN_TOTAL"] = "percent_of_column_total";
    TableCalculationTemplateType["RANK_IN_COLUMN"] = "rank_in_column";
    TableCalculationTemplateType["RUNNING_TOTAL"] = "running_total";
    TableCalculationTemplateType["WINDOW_FUNCTION"] = "window_function";
})(TableCalculationTemplateType || (exports.TableCalculationTemplateType = TableCalculationTemplateType = {}));
var FieldType;
(function (FieldType) {
    FieldType["METRIC"] = "metric";
    FieldType["DIMENSION"] = "dimension";
})(FieldType || (exports.FieldType = FieldType = {}));
// This type check is a little fragile because it's based on
// 'displayName'. Ideally these would all have fieldTypes.
const isTableCalculation = (item) => item
    ? !(0, exports.isCustomDimension)(item) &&
        (!!('sql' in item && item.sql) ||
            !!('template' in item && item.template) ||
            !!('formula' in item && item.formula)) &&
        !('description' in item) &&
        !('tableName' in item) &&
        'displayName' in item
    : false;
exports.isTableCalculation = isTableCalculation;
const isSqlTableCalculation = (calc) => !!calc && 'sql' in calc && !!calc.sql && calc.sql.length > 0;
exports.isSqlTableCalculation = isSqlTableCalculation;
const isTemplateTableCalculation = (calc) => !!calc && 'template' in calc && !!calc.template;
exports.isTemplateTableCalculation = isTemplateTableCalculation;
const isFormulaTableCalculation = (calc) => !!calc && 'formula' in calc && !!calc.formula && calc.formula.length > 0;
exports.isFormulaTableCalculation = isFormulaTableCalculation;
const isField = (field) => field ? !!field.fieldType : false;
exports.isField = isField;
const convertFieldRefToFieldId = (fieldRef, fallbackTableName) => {
    const parts = fieldRef.split('.');
    if (parts.length !== 2) {
        if (fallbackTableName) {
            return `${fallbackTableName}_${fieldRef}`;
        }
        throw new errors_1.CompileError(`Table calculation contains an invalid reference: ${fieldRef}. References must be of the format "table.field"`, {});
    }
    const [tableName, fieldName] = parts;
    return `${tableName}_${fieldName}`;
};
exports.convertFieldRefToFieldId = convertFieldRefToFieldId;
var DimensionType;
(function (DimensionType) {
    DimensionType["STRING"] = "string";
    DimensionType["NUMBER"] = "number";
    DimensionType["TIMESTAMP"] = "timestamp";
    DimensionType["DATE"] = "date";
    DimensionType["BOOLEAN"] = "boolean";
})(DimensionType || (exports.DimensionType = DimensionType = {}));
/**
 * Type guard to check if a compiled field has a compilation error.
 */
const hasFieldCompilationError = (field) => field.compilationError !== undefined;
exports.hasFieldCompilationError = hasFieldCompilationError;
const isDimension = (field) => (0, exports.isField)(field) && field.fieldType === FieldType.DIMENSION;
exports.isDimension = isDimension;
const isTimeBasedDimension = (item) => (0, exports.isDimension)(item) &&
    (item.type === DimensionType.DATE || item.type === DimensionType.TIMESTAMP);
exports.isTimeBasedDimension = isTimeBasedDimension;
const getFieldRef = (field) => `${field.table}.${field.name}`;
exports.getFieldRef = getFieldRef;
const getFieldLabel = (field) => `${field.tableLabel} ${field.label}`;
exports.getFieldLabel = getFieldLabel;
var MetricType;
(function (MetricType) {
    MetricType["PERCENTILE"] = "percentile";
    MetricType["AVERAGE"] = "average";
    MetricType["COUNT"] = "count";
    MetricType["COUNT_DISTINCT"] = "count_distinct";
    MetricType["SUM"] = "sum";
    MetricType["SUM_DISTINCT"] = "sum_distinct";
    MetricType["AVERAGE_DISTINCT"] = "average_distinct";
    MetricType["MIN"] = "min";
    MetricType["MAX"] = "max";
    MetricType["PERCENT_OF_PREVIOUS"] = "percent_of_previous";
    MetricType["PERCENT_OF_TOTAL"] = "percent_of_total";
    MetricType["RUNNING_TOTAL"] = "running_total";
    MetricType["NUMBER"] = "number";
    MetricType["MEDIAN"] = "median";
    MetricType["STRING"] = "string";
    MetricType["DATE"] = "date";
    MetricType["TIMESTAMP"] = "timestamp";
    MetricType["BOOLEAN"] = "boolean";
})(MetricType || (exports.MetricType = MetricType = {}));
var Format;
(function (Format) {
    Format["KM"] = "km";
    Format["MI"] = "mi";
    Format["USD"] = "usd";
    Format["GBP"] = "gbp";
    Format["EUR"] = "eur";
    Format["JPY"] = "jpy";
    Format["DKK"] = "dkk";
    Format["ID"] = "id";
    Format["PERCENT"] = "percent";
})(Format || (exports.Format = Format = {}));
const isFormat = (value) => !!value && Object.values(Format).includes(value);
exports.isFormat = isFormat;
const parseMetricType = (metricType) => {
    switch (metricType) {
        case 'percentile':
            return MetricType.PERCENTILE;
        case 'median':
            return MetricType.MEDIAN;
        case 'average':
            return MetricType.AVERAGE;
        case 'count':
            return MetricType.COUNT;
        case 'count_distinct':
            return MetricType.COUNT_DISTINCT;
        case 'sum':
            return MetricType.SUM;
        case 'sum_distinct':
            return MetricType.SUM_DISTINCT;
        case 'average_distinct':
            return MetricType.AVERAGE_DISTINCT;
        case 'min':
            return MetricType.MIN;
        case 'max':
            return MetricType.MAX;
        case 'number':
            return MetricType.NUMBER;
        case 'string':
            return MetricType.STRING;
        case 'date':
            return MetricType.DATE;
        case 'timestamp':
            return MetricType.TIMESTAMP;
        case 'boolean':
            return MetricType.BOOLEAN;
        case 'percent_of_previous':
            return MetricType.PERCENT_OF_PREVIOUS;
        case 'percent_of_total':
            return MetricType.PERCENT_OF_TOTAL;
        case 'running_total':
            return MetricType.RUNNING_TOTAL;
        default:
            throw new Error(`Cannot parse dbt metric with type '${metricType}'`);
    }
};
exports.parseMetricType = parseMetricType;
const NonAggregateMetricTypes = [
    MetricType.STRING,
    MetricType.NUMBER,
    MetricType.DATE,
    MetricType.TIMESTAMP,
    MetricType.BOOLEAN,
];
exports.PostCalculationMetricTypes = [
    MetricType.PERCENT_OF_PREVIOUS,
    MetricType.PERCENT_OF_TOTAL,
    MetricType.RUNNING_TOTAL,
];
const isMetric = (field) => field
    ? 'fieldType' in field && field.fieldType === FieldType.METRIC
    : false;
exports.isMetric = isMetric;
const isNonAggregateMetric = (field) => (0, exports.isMetric)(field) && NonAggregateMetricTypes.includes(field.type);
exports.isNonAggregateMetric = isNonAggregateMetric;
exports.AggregateMetricTypes = [
    MetricType.SUM,
    MetricType.COUNT,
    MetricType.COUNT_DISTINCT,
    MetricType.AVERAGE,
    MetricType.MIN,
    MetricType.MAX,
    MetricType.MEDIAN,
    MetricType.PERCENTILE,
];
const isAggregateMetricType = (type) => exports.AggregateMetricTypes.includes(type);
exports.isAggregateMetricType = isAggregateMetricType;
const isNonAggregateMetricType = (type) => NonAggregateMetricTypes.includes(type);
exports.isNonAggregateMetricType = isNonAggregateMetricType;
const isPostCalculationMetricType = (type) => exports.PostCalculationMetricTypes.includes(type);
exports.isPostCalculationMetricType = isPostCalculationMetricType;
const isPostCalculationMetric = (field) => (0, exports.isMetric)(field) && (0, exports.isPostCalculationMetricType)(field.type);
exports.isPostCalculationMetric = isPostCalculationMetric;
const isCompiledMetric = (field) => (0, exports.isMetric)(field) && 'compiledSql' in field;
exports.isCompiledMetric = isCompiledMetric;
const isFilterableDimension = (dimension) => !!dimension &&
    [
        DimensionType.STRING,
        DimensionType.NUMBER,
        DimensionType.DATE,
        DimensionType.TIMESTAMP,
        DimensionType.BOOLEAN,
    ].includes(dimension.type);
exports.isFilterableDimension = isFilterableDimension;
const isFilterableField = (field) => ((0, exports.isDimension)(field) && (0, exports.isFilterableDimension)(field)) ||
    (0, exports.isCustomSqlDimension)(field) ||
    (0, exports.isMetric)(field) ||
    (0, exports.isTableCalculation)(field);
exports.isFilterableField = isFilterableField;
const isFilterableItem = (item) => (0, exports.isDimension)(item) ? (0, exports.isFilterableDimension)(item) : true;
exports.isFilterableItem = isFilterableItem;
const defaultSql = (columnName) => 
// eslint-disable-next-line no-useless-escape
`\$\{TABLE\}.${columnName}`;
exports.defaultSql = defaultSql;
const capitalize = (word) => word ? `${word.charAt(0).toUpperCase()}${word.slice(1).toLowerCase()}` : '';
exports.capitalize = capitalize;
const friendlyName = (text) => {
    if (text === '') {
        return '';
    }
    // Use lodash's words function with a safe custom pattern
    // Pattern explanation (avoids backtracking):
    // - [A-Z][a-z]+: Uppercase letter followed by 1+ lowercase (avoids * quantifier)
    // - [a-z]+: One or more lowercase letters
    // - [0-9]+[a-z]+: Digits followed by letters (keeps them together like "1field")
    // - [0-9]+: Just digits
    // - [A-Z]+: Multiple uppercase letters
    const safePattern = /[A-Z][a-z]+|[a-z]+|[0-9]+[a-z]+|[0-9]+|[A-Z]+/g;
    const extractedWords = (0, words_1.default)(text, safePattern);
    if (extractedWords.length === 0) {
        return '';
    }
    // Join words with spaces and convert to sentence case
    // (only first letter capitalized, rest lowercase)
    const joined = extractedWords.join(' ').toLowerCase();
    return joined.charAt(0).toUpperCase() + joined.slice(1);
};
exports.friendlyName = friendlyName;
const isSummable = (item) => {
    if (!item) {
        return false;
    }
    if ((0, exports.isTableCalculation)(item)) {
        return false;
    }
    if ((0, exports.isCustomDimension)(item)) {
        return false;
    }
    const numericTypes = [MetricType.COUNT, MetricType.SUM];
    const isNumberDimension = (0, exports.isDimension)(item) && item.type === DimensionType.NUMBER;
    const isNumbericType = numericTypes.includes(item.type) || isNumberDimension;
    const isPercent = item.format === 'percent';
    const isDatePart = (0, exports.isDimension)(item) && item.timeInterval;
    return isNumbericType && !isPercent && !isDatePart;
};
exports.isSummable = isSummable;
const SIByteCompacts = [
    Compact.KILOBYTES,
    Compact.MEGABYTES,
    Compact.GIGABYTES,
    Compact.TERABYTES,
    Compact.PETABYTES,
];
exports.IECByteCompacts = [
    Compact.KIBIBYTES,
    Compact.MEBIBYTES,
    Compact.GIBIBYTES,
    Compact.TEBIBYTES,
    Compact.PEBIBYTES,
];
function getCompactOptionsForFormatType(type) {
    if (type === CustomFormatType.BYTES_IEC)
        return exports.IECByteCompacts;
    if (type === CustomFormatType.BYTES_SI)
        return SIByteCompacts;
    return [
        Compact.THOUSANDS,
        Compact.MILLIONS,
        Compact.BILLIONS,
        Compact.TRILLIONS,
    ];
}
//# sourceMappingURL=field.js.map