"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const parameters_1 = require("../../compiler/parameters");
const field_1 = require("../../types/field");
const filter_1 = require("../../types/filter");
const item_1 = require("../../utils/item");
const dimensionEligibility_1 = require("./dimensionEligibility");
const metricEligibility_1 = require("./metricEligibility");
const makeDimension = (overrides) => ({
    ...overrides,
    fieldType: field_1.FieldType.DIMENSION,
    type: field_1.DimensionType.NUMBER,
    name: overrides.name,
    label: overrides.label ?? overrides.name,
    table: overrides.table ?? 'orders',
    tableLabel: overrides.tableLabel ?? 'Orders',
    sql: overrides.sql ?? '${TABLE}.amount',
    compiledSql: overrides.compiledSql ?? overrides.sql ?? '"orders".amount',
    parameterReferences: overrides.parameterReferences ??
        (0, parameters_1.getParameterReferences)(overrides.sql ?? '${TABLE}.amount'),
    tablesReferences: overrides.tablesReferences ?? ['orders'],
    hidden: overrides.hidden ?? false,
});
const makeMetric = (overrides) => ({
    ...overrides,
    fieldType: field_1.FieldType.METRIC,
    type: overrides.type,
    name: overrides.name,
    label: overrides.label ?? overrides.name,
    table: overrides.table ?? 'orders',
    tableLabel: overrides.tableLabel ?? 'Orders',
    sql: overrides.sql ?? 'sum(${TABLE}.amount)',
    compiledSql: overrides.compiledSql ?? overrides.sql ?? 'SUM("orders".amount)',
    parameterReferences: overrides.parameterReferences ??
        (0, parameters_1.getParameterReferences)(overrides.sql ?? 'sum(${TABLE}.amount)'),
    tablesReferences: overrides.tablesReferences ?? ['orders'],
    hidden: overrides.hidden ?? false,
});
const makeTables = ({ dimensions, metrics, }) => [...dimensions, ...metrics].reduce((acc, field) => {
    acc[field.table] = acc[field.table] ?? {
        name: field.table,
        originalName: field.table === 'cstmr' ? 'customers' : undefined,
        dimensions: {},
        metrics: {},
    };
    if (field.fieldType === field_1.FieldType.DIMENSION) {
        acc[field.table].dimensions[field.name] = field;
    }
    else {
        acc[field.table].metrics[field.name] = field;
    }
    return acc;
}, {});
describe('analyzePreAggregateDerivedMetricEligibility', () => {
    it('classifies a custom sql metric over eligible dimensions as eligible', () => {
        const amount = makeDimension({
            name: 'amount',
            sql: '${TABLE}.amount',
            compiledSql: '"orders".amount',
        });
        const shippingCost = makeDimension({
            name: 'shipping_cost',
            sql: '${TABLE}.shipping_cost',
            compiledSql: '"orders".shipping_cost',
        });
        const totalValue = makeMetric({
            name: 'total_value',
            type: field_1.MetricType.SUM,
            sql: '${amount} + ${shipping_cost}',
            compiledSql: 'SUM("orders".amount + "orders".shipping_cost)',
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: totalValue,
            tables: makeTables({
                dimensions: [amount, shippingCost],
                metrics: [totalValue],
            }),
        })).toEqual({
            isEligible: true,
            referencedDimensionFieldIds: [
                (0, item_1.getItemId)(amount),
                (0, item_1.getItemId)(shippingCost),
            ],
            referencedMetricFieldIds: [(0, item_1.getItemId)(totalValue)],
        });
    });
    it('classifies a metric with recursive metric dependencies as eligible when all dependencies are safe', () => {
        const amount = makeDimension({
            name: 'amount',
            sql: '${TABLE}.amount',
            compiledSql: '"orders".amount',
        });
        const orderValue = makeMetric({
            name: 'order_value',
            type: field_1.MetricType.SUM,
            sql: '${amount}',
            compiledSql: 'SUM("orders".amount)',
        });
        const doubledOrderValue = makeMetric({
            name: 'doubled_order_value',
            type: field_1.MetricType.NUMBER,
            sql: '${order_value} * 2',
            compiledSql: '(SUM("orders".amount)) * 2',
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: doubledOrderValue,
            tables: makeTables({
                dimensions: [amount],
                metrics: [orderValue, doubledOrderValue],
            }),
        })).toEqual({
            isEligible: true,
            referencedDimensionFieldIds: [(0, item_1.getItemId)(amount)],
            referencedMetricFieldIds: [
                (0, item_1.getItemId)(doubledOrderValue),
                (0, item_1.getItemId)(orderValue),
            ],
        });
    });
    it('resolves metric references through a joined table original name', () => {
        const customerFirstName = makeDimension({
            name: 'first_name',
            table: 'cstmr',
            sql: '${TABLE}.first_name',
            compiledSql: '"customers".first_name',
        });
        const customerNameCount = makeMetric({
            name: 'customer_name_count',
            table: 'orders',
            type: field_1.MetricType.COUNT,
            sql: '${customers.first_name}',
            compiledSql: 'COUNT("customers".first_name)',
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: customerNameCount,
            tables: makeTables({
                dimensions: [customerFirstName],
                metrics: [customerNameCount],
            }),
        })).toEqual({
            isEligible: true,
            referencedDimensionFieldIds: [(0, item_1.getItemId)(customerFirstName)],
            referencedMetricFieldIds: [(0, item_1.getItemId)(customerNameCount)],
        });
    });
    it('rejects when a metric references a parameter directly', () => {
        const regionAwareRevenue = makeMetric({
            name: 'region_aware_revenue',
            type: field_1.MetricType.SUM,
            sql: `
                CASE
                    WHEN \${lightdash.parameters.orders.region} = 'EMEA' THEN \${TABLE}.amount
                    ELSE 0
                END
            `,
            parameterReferences: ['orders.region'],
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: regionAwareRevenue,
            tables: makeTables({
                dimensions: [],
                metrics: [regionAwareRevenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.PARAMETER_REFERENCES,
            ineligibleMetricFieldId: (0, item_1.getItemId)(regionAwareRevenue),
            referencedDimensionFieldIds: [],
            referencedMetricFieldIds: [(0, item_1.getItemId)(regionAwareRevenue)],
        });
    });
    it('rejects when a metric sql contains a direct user attribute reference', () => {
        const regionAwareRevenue = makeMetric({
            name: 'region_aware_revenue',
            type: field_1.MetricType.SUM,
            sql: `
                CASE
                    WHEN \${lightdash.attributes.region} = 'EMEA' THEN \${TABLE}.amount
                    ELSE 0
                END
            `,
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: regionAwareRevenue,
            tables: makeTables({
                dimensions: [],
                metrics: [regionAwareRevenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.USER_ATTRIBUTES,
            ineligibleMetricFieldId: (0, item_1.getItemId)(regionAwareRevenue),
            referencedDimensionFieldIds: [],
            referencedMetricFieldIds: [(0, item_1.getItemId)(regionAwareRevenue)],
        });
    });
    it('rejects when a metric sql contains a direct intrinsic user reference', () => {
        const emailAwareRevenue = makeMetric({
            name: 'email_aware_revenue',
            type: field_1.MetricType.SUM,
            sql: `
                CASE
                    WHEN \${ld.user.email} IS NOT NULL THEN \${TABLE}.amount
                    ELSE 0
                END
            `,
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: emailAwareRevenue,
            tables: makeTables({
                dimensions: [],
                metrics: [emailAwareRevenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.USER_ATTRIBUTES,
            ineligibleMetricFieldId: (0, item_1.getItemId)(emailAwareRevenue),
            referencedDimensionFieldIds: [],
            referencedMetricFieldIds: [(0, item_1.getItemId)(emailAwareRevenue)],
        });
    });
    it('rejects when a referenced dimension is ineligible', () => {
        const parameterizedAmount = makeDimension({
            name: 'parameterized_amount',
            sql: `
                CASE
                    WHEN \${lightdash.parameters.orders.region} = 'EMEA' THEN \${TABLE}.amount
                    ELSE NULL
                END
            `,
            parameterReferences: ['orders.region'],
        });
        const revenue = makeMetric({
            name: 'revenue',
            type: field_1.MetricType.SUM,
            sql: '${parameterized_amount}',
            compiledSql: 'SUM(CASE WHEN ... END)',
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: revenue,
            tables: makeTables({
                dimensions: [parameterizedAmount],
                metrics: [revenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.DIMENSION_DEPENDENCY_INELIGIBLE,
            ineligibleMetricFieldId: (0, item_1.getItemId)(revenue),
            ineligibleDimensionFieldId: (0, item_1.getItemId)(parameterizedAmount),
            ineligibleDimensionReason: dimensionEligibility_1.PreAggregateDerivedDimensionIneligibilityReason.PARAMETER_REFERENCES,
            referencedDimensionFieldIds: [(0, item_1.getItemId)(parameterizedAmount)],
            referencedMetricFieldIds: [(0, item_1.getItemId)(revenue)],
        });
    });
    it('rejects when a recursive metric dependency is ineligible', () => {
        const parameterizedRevenue = makeMetric({
            name: 'parameterized_revenue',
            type: field_1.MetricType.SUM,
            sql: `
                CASE
                    WHEN \${lightdash.parameters.orders.region} = 'EMEA' THEN \${TABLE}.amount
                    ELSE 0
                END
            `,
            parameterReferences: ['orders.region'],
        });
        const doubledRevenue = makeMetric({
            name: 'doubled_revenue',
            type: field_1.MetricType.NUMBER,
            sql: '${parameterized_revenue} * 2',
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: doubledRevenue,
            tables: makeTables({
                dimensions: [],
                metrics: [parameterizedRevenue, doubledRevenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.METRIC_DEPENDENCY_INELIGIBLE,
            ineligibleMetricFieldId: (0, item_1.getItemId)(parameterizedRevenue),
            referencedDimensionFieldIds: [],
            referencedMetricFieldIds: [
                (0, item_1.getItemId)(doubledRevenue),
                (0, item_1.getItemId)(parameterizedRevenue),
            ],
        });
    });
    it('rejects when a metric filter references an ineligible dimension', () => {
        const amount = makeDimension({
            name: 'amount',
            sql: '${TABLE}.amount',
            compiledSql: '"orders".amount',
        });
        const parameterizedStatus = makeDimension({
            name: 'parameterized_status',
            sql: `
                CASE
                    WHEN \${lightdash.parameters.orders.region} = 'EMEA' THEN \${TABLE}.status
                    ELSE NULL
                END
            `,
            parameterReferences: ['orders.region'],
        });
        const filteredRevenue = makeMetric({
            name: 'filtered_revenue',
            type: field_1.MetricType.SUM,
            sql: '${amount}',
            compiledSql: 'SUM("orders".amount)',
            filters: [
                {
                    id: 'metric-filter',
                    target: {
                        fieldRef: 'parameterized_status',
                    },
                    operator: filter_1.FilterOperator.EQUALS,
                    values: ['completed'],
                },
            ],
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: filteredRevenue,
            tables: makeTables({
                dimensions: [amount, parameterizedStatus],
                metrics: [filteredRevenue],
            }),
        })).toEqual({
            isEligible: false,
            reason: metricEligibility_1.PreAggregateDerivedMetricIneligibilityReason.FILTER_DIMENSION_INELIGIBLE,
            ineligibleMetricFieldId: (0, item_1.getItemId)(filteredRevenue),
            ineligibleDimensionFieldId: (0, item_1.getItemId)(parameterizedStatus),
            ineligibleDimensionReason: dimensionEligibility_1.PreAggregateDerivedDimensionIneligibilityReason.PARAMETER_REFERENCES,
            referencedDimensionFieldIds: [
                (0, item_1.getItemId)(amount),
                (0, item_1.getItemId)(parameterizedStatus),
            ],
            referencedMetricFieldIds: [(0, item_1.getItemId)(filteredRevenue)],
        });
    });
    it('resolves metric filter dimensions case-insensitively', () => {
        const orderDateDay = makeDimension({
            name: 'order_date_day',
            table: 'orders',
            sql: '${TABLE}.order_date',
            compiledSql: 'DATE_TRUNC(\'DAY\', "orders".order_date)',
        });
        const filteredRevenue = makeMetric({
            name: 'filtered_revenue',
            type: field_1.MetricType.SUM,
            sql: '${TABLE}.amount',
            compiledSql: 'SUM("orders".amount)',
            filters: [
                {
                    id: 'metric-filter',
                    target: {
                        fieldRef: 'order_date_DAY',
                    },
                    operator: filter_1.FilterOperator.EQUALS,
                    values: ['2024-01-01'],
                },
            ],
        });
        expect((0, metricEligibility_1.analyzePreAggregateDerivedMetricEligibility)({
            metric: filteredRevenue,
            tables: makeTables({
                dimensions: [orderDateDay],
                metrics: [filteredRevenue],
            }),
        })).toEqual({
            isEligible: true,
            referencedDimensionFieldIds: [(0, item_1.getItemId)(orderDateDay)],
            referencedMetricFieldIds: [(0, item_1.getItemId)(filteredRevenue)],
        });
    });
});
//# sourceMappingURL=metricEligibility.test.js.map