"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseDbtPreAggregates = exports.parseDbtPreAggregateDef = void 0;
const tslib_1 = require("tslib");
const castArray_1 = tslib_1.__importDefault(require("lodash/castArray"));
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
const isPlainObject_1 = tslib_1.__importDefault(require("lodash/isPlainObject"));
const isString_1 = tslib_1.__importDefault(require("lodash/isString"));
const keys_1 = tslib_1.__importDefault(require("lodash/keys"));
const errors_1 = require("../types/errors");
const filterGrammar_1 = require("../types/filterGrammar");
const timeFrames_1 = require("../types/timeFrames");
const PRE_AGGREGATE_NAME_PATTERN = /^[a-zA-Z0-9_]+$/;
const parsePreAggregateGranularity = (granularity, modelName, preAggregateName) => {
    const normalized = granularity.trim().toUpperCase();
    if (!Object.values(timeFrames_1.TimeFrames).includes(normalized)) {
        throw new errors_1.ParseError(`Invalid pre_aggregate granularity "${granularity}" for "${preAggregateName}" in model "${modelName}"`);
    }
    return normalized;
};
const parsePreAggregateStringArray = (value, fieldName, modelName, preAggregateName) => {
    if (!Array.isArray(value) || value.length === 0) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" must define a non-empty "${fieldName}" array`);
    }
    return value.map((item) => {
        if (typeof item !== 'string' || item.trim().length === 0) {
            throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" has invalid "${fieldName}" value`);
        }
        return item.trim();
    });
};
const parseMaterializationRoleAttributes = (value, modelName, preAggregateName) => {
    if (!(0, isPlainObject_1.default)(value) || (0, isEmpty_1.default)(value)) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" has invalid "materialization_role.attributes". Expected a non-empty object.`);
    }
    const attributes = value;
    return Object.entries(attributes).reduce((acc, [attributeName, attributeValue]) => {
        const attributeValues = (0, castArray_1.default)(attributeValue);
        if (!(0, isEmpty_1.default)(attributeValues) && attributeValues.every(isString_1.default)) {
            acc[attributeName] = attributeValues;
            return acc;
        }
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" has invalid "materialization_role.attributes.${attributeName}" value`);
    }, {});
};
const parseMaterializationRole = (materializationRole, modelName, preAggregateName) => {
    if (!(0, isPlainObject_1.default)(materializationRole)) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" has invalid "materialization_role". Expected an object.`);
    }
    const { email, attributes, ...unknownFields } = materializationRole;
    const unknownIntrinsicFields = (0, keys_1.default)(unknownFields);
    if (!(0, isEmpty_1.default)(unknownIntrinsicFields)) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" has unsupported "materialization_role" fields: ${unknownIntrinsicFields.join(', ')}`);
    }
    if (!(0, isString_1.default)(email) || email.trim().length === 0) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" must define a non-empty "materialization_role.email"`);
    }
    if (attributes === undefined) {
        throw new errors_1.ParseError(`Pre-aggregate "${preAggregateName}" in model "${modelName}" must define "materialization_role.attributes" when "materialization_role" is set`);
    }
    return {
        email: email.trim(),
        attributes: parseMaterializationRoleAttributes(attributes, modelName, preAggregateName),
    };
};
const parseDbtPreAggregateDef = (preAggregate, modelName) => {
    const safePreAggregate = preAggregate;
    const name = typeof safePreAggregate?.name === 'string'
        ? safePreAggregate.name.trim()
        : '';
    if (name.length === 0) {
        throw new errors_1.ParseError(`Pre-aggregate in model "${modelName}" must define a non-empty "name"`);
    }
    if (!PRE_AGGREGATE_NAME_PATTERN.test(name)) {
        throw new errors_1.ParseError(`Pre-aggregate "${name}" in model "${modelName}" has invalid name. Names must contain only letters, numbers, and underscores.`);
    }
    const dimensions = parsePreAggregateStringArray(safePreAggregate?.dimensions, 'dimensions', modelName, name);
    const metrics = parsePreAggregateStringArray(safePreAggregate?.metrics, 'metrics', modelName, name);
    const timeDimension = typeof safePreAggregate?.time_dimension === 'string'
        ? safePreAggregate.time_dimension.trim()
        : undefined;
    if (typeof safePreAggregate?.time_dimension === 'string' &&
        timeDimension === '') {
        throw new errors_1.ParseError(`Pre-aggregate "${name}" in model "${modelName}" has invalid "time_dimension"`);
    }
    const granularity = typeof safePreAggregate?.granularity === 'string'
        ? parsePreAggregateGranularity(safePreAggregate.granularity, modelName, name)
        : undefined;
    if ((timeDimension && !granularity) || (!timeDimension && granularity)) {
        throw new errors_1.ParseError(`Pre-aggregate "${name}" in model "${modelName}" must define both "time_dimension" and "granularity" together`);
    }
    const maxRows = typeof safePreAggregate?.max_rows === 'number'
        ? safePreAggregate.max_rows
        : undefined;
    if (maxRows !== undefined && (!Number.isInteger(maxRows) || maxRows <= 0)) {
        throw new errors_1.ParseError(`Pre-aggregate "${name}" in model "${modelName}" has invalid "max_rows". Must be a positive integer.`);
    }
    const materializationRole = safePreAggregate?.materialization_role !== undefined
        ? parseMaterializationRole(safePreAggregate.materialization_role, modelName, name)
        : undefined;
    const filters = safePreAggregate?.filters
        ? (0, filterGrammar_1.parseFilters)(safePreAggregate.filters)
        : undefined;
    return {
        name,
        dimensions,
        metrics,
        ...(filters ? { filters } : {}),
        ...(timeDimension ? { timeDimension } : {}),
        ...(granularity ? { granularity } : {}),
        ...(maxRows !== undefined ? { maxRows } : {}),
        ...(safePreAggregate.refresh?.cron
            ? { refresh: { cron: safePreAggregate.refresh.cron } }
            : {}),
        ...(materializationRole ? { materializationRole } : {}),
    };
};
exports.parseDbtPreAggregateDef = parseDbtPreAggregateDef;
const parseDbtPreAggregates = (preAggregates, modelName) => {
    if (preAggregates === undefined) {
        return [];
    }
    if (!Array.isArray(preAggregates)) {
        throw new errors_1.ParseError(`Model "${modelName}" has invalid "pre_aggregates" metadata. Expected an array.`);
    }
    return preAggregates.map((preAggregate) => (0, exports.parseDbtPreAggregateDef)(preAggregate, modelName));
};
exports.parseDbtPreAggregates = parseDbtPreAggregates;
//# sourceMappingURL=definition.js.map