"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMetricOverridesWithPopInheritance = exports.getGranularityRank = exports.buildPopAdditionalMetric = exports.hasPeriodOverPeriodAdditionalMetricWithConfig = exports.getPopComparisonConfigKey = exports.getPopPeriodLabel = exports.buildPopAdditionalMetricName = exports.hashPopComparisonConfigKeyToSuffix = exports.hashStringToBase36 = exports.isSupportedPeriodOverPeriodGranularity = exports.periodOverPeriodGranularityLabels = exports.validPeriodOverPeriodGranularities = void 0;
const uuid_1 = require("uuid");
const item_1 = require("../utils/item");
const timeFrames_1 = require("../utils/timeFrames");
const metricQuery_1 = require("./metricQuery");
const timeFrames_2 = require("./timeFrames");
exports.validPeriodOverPeriodGranularities = [
    timeFrames_2.TimeFrames.DAY,
    timeFrames_2.TimeFrames.WEEK,
    timeFrames_2.TimeFrames.MONTH,
    timeFrames_2.TimeFrames.QUARTER,
    timeFrames_2.TimeFrames.YEAR,
];
exports.periodOverPeriodGranularityLabels = {
    [timeFrames_2.TimeFrames.YEAR]: 'Year',
    [timeFrames_2.TimeFrames.QUARTER]: 'Quarter',
    [timeFrames_2.TimeFrames.MONTH]: 'Month',
    [timeFrames_2.TimeFrames.WEEK]: 'Week',
    [timeFrames_2.TimeFrames.DAY]: 'Day',
    [timeFrames_2.TimeFrames.HOUR]: 'Hour',
    [timeFrames_2.TimeFrames.MINUTE]: 'Minute',
    [timeFrames_2.TimeFrames.SECOND]: 'Second',
    [timeFrames_2.TimeFrames.MILLISECOND]: 'Millisecond',
    [timeFrames_2.TimeFrames.RAW]: 'Raw',
    [timeFrames_2.TimeFrames.DAY_OF_WEEK_INDEX]: 'Day of Week Index',
    [timeFrames_2.TimeFrames.DAY_OF_MONTH_NUM]: 'Day of Month',
    [timeFrames_2.TimeFrames.DAY_OF_YEAR_NUM]: 'Day of Year',
    [timeFrames_2.TimeFrames.WEEK_NUM]: 'Week Number',
    [timeFrames_2.TimeFrames.MONTH_NUM]: 'Month Number',
    [timeFrames_2.TimeFrames.QUARTER_NUM]: 'Quarter Number',
    [timeFrames_2.TimeFrames.YEAR_NUM]: 'Year Number',
    [timeFrames_2.TimeFrames.DAY_OF_WEEK_NAME]: 'Day of Week Name',
    [timeFrames_2.TimeFrames.MONTH_NAME]: 'Month Name',
    [timeFrames_2.TimeFrames.QUARTER_NAME]: 'Quarter Name',
    [timeFrames_2.TimeFrames.HOUR_OF_DAY_NUM]: 'Hour of Day',
    [timeFrames_2.TimeFrames.MINUTE_OF_HOUR_NUM]: 'Minute of Hour',
};
const isSupportedPeriodOverPeriodGranularity = (granularity) => exports.validPeriodOverPeriodGranularities.includes(granularity);
exports.isSupportedPeriodOverPeriodGranularity = isSupportedPeriodOverPeriodGranularity;
const hashStringToBase36 = (input) => {
    // Deterministic, non-cryptographic hash (no deps).
    // Polynomial rolling hash modulo a large prime (lint-safe: no bitwise ops).
    const MODULUS = 2147483647; // 2^31 - 1
    const BASE = 31;
    let hash = 0;
    for (let i = 0; i < input.length; i += 1) {
        hash = (hash * BASE + input.charCodeAt(i)) % MODULUS;
    }
    // pad for nicer/consistent suffix length
    return hash.toString(36).padStart(6, '0');
};
exports.hashStringToBase36 = hashStringToBase36;
const hashPopComparisonConfigKeyToSuffix = (configKey) => (0, exports.hashStringToBase36)(configKey).padStart(8, '0');
exports.hashPopComparisonConfigKeyToSuffix = hashPopComparisonConfigKeyToSuffix;
const buildPopAdditionalMetricName = ({ baseMetricName, timeDimensionId, granularity, periodOffset, }) => `${baseMetricName}__pop__${String(granularity).toLowerCase()}_${periodOffset}__${(0, exports.hashStringToBase36)(`${timeDimensionId}|${granularity}|${periodOffset}`)}`;
exports.buildPopAdditionalMetricName = buildPopAdditionalMetricName;
const getPopPeriodLabel = (granularity, periodOffset) => {
    const label = timeFrames_1.timeFrameConfigs[granularity]?.getLabel() || granularity;
    return periodOffset === 1
        ? `Previous ${String(label).toLowerCase()}`
        : `${periodOffset} ${String(label).toLowerCase()}s ago`;
};
exports.getPopPeriodLabel = getPopPeriodLabel;
const getPopComparisonConfigKey = ({ timeDimensionId, granularity, periodOffset, }) => JSON.stringify([
    'pop:v1',
    timeDimensionId,
    granularity,
    periodOffset,
]);
exports.getPopComparisonConfigKey = getPopComparisonConfigKey;
const hasPeriodOverPeriodAdditionalMetricWithConfig = ({ additionalMetrics, baseMetricId, timeDimensionId, granularity, periodOffset, }) => additionalMetrics.some((am) => (0, metricQuery_1.isPeriodOverPeriodAdditionalMetric)(am) &&
    am.baseMetricId === baseMetricId &&
    am.timeDimensionId === timeDimensionId &&
    am.granularity === granularity &&
    am.periodOffset === periodOffset);
exports.hasPeriodOverPeriodAdditionalMetricWithConfig = hasPeriodOverPeriodAdditionalMetricWithConfig;
const buildPopAdditionalMetric = ({ metric, timeDimensionId, granularity, periodOffset, }) => {
    const baseMetricId = (0, item_1.getItemId)(metric);
    const popName = (0, exports.buildPopAdditionalMetricName)({
        baseMetricName: metric.name,
        timeDimensionId,
        granularity,
        periodOffset,
    });
    const popMetricId = (0, item_1.getItemId)({ table: metric.table, name: popName });
    const additionalMetric = {
        uuid: (0, uuid_1.v4)(),
        table: metric.table,
        name: popName,
        label: `${metric.label} (${(0, exports.getPopPeriodLabel)(granularity, periodOffset)})`,
        description: metric.description,
        type: metric.type,
        sql: metric.sql,
        hidden: true,
        round: metric.round,
        compact: metric.compact,
        format: metric.format,
        formatOptions: metric.formatOptions,
        generationType: 'periodOverPeriod',
        baseMetricId,
        timeDimensionId,
        granularity,
        periodOffset,
    };
    return { additionalMetric, metricId: popMetricId };
};
exports.buildPopAdditionalMetric = buildPopAdditionalMetric;
// Granularity order from finest to coarsest (lower index = finer)
const GRANULARITY_ORDER = [
    timeFrames_2.TimeFrames.DAY,
    timeFrames_2.TimeFrames.WEEK,
    timeFrames_2.TimeFrames.MONTH,
    timeFrames_2.TimeFrames.QUARTER,
    timeFrames_2.TimeFrames.YEAR,
];
const getGranularityRank = (granularity) => {
    const index = GRANULARITY_ORDER.indexOf(granularity);
    return index === -1 ? Infinity : index;
};
exports.getGranularityRank = getGranularityRank;
/**
 * Returns a `MetricOverrides` map where period-over-period metric IDs
 * inherit the format override from their base metric when they don't have
 * one of their own. Leaves any existing PoP overrides untouched.
 *
 * This lets downstream consumers (query services, frontend tables) use a
 * single resolved override map without needing to know about PoP metrics.
 */
const getMetricOverridesWithPopInheritance = (metricQuery) => {
    const baseOverrides = metricQuery.metricOverrides ?? {};
    const additionalMetrics = metricQuery.additionalMetrics ?? [];
    const resolved = { ...baseOverrides };
    for (const am of additionalMetrics) {
        if ((0, metricQuery_1.isPeriodOverPeriodAdditionalMetric)(am)) {
            const popId = (0, item_1.getItemId)(am);
            const baseOverride = baseOverrides[am.baseMetricId];
            if (!resolved[popId] && baseOverride) {
                resolved[popId] = baseOverride;
            }
        }
    }
    return resolved;
};
exports.getMetricOverridesWithPopInheritance = getMetricOverridesWithPopInheritance;
//# sourceMappingURL=periodOverPeriodComparison.js.map