import { GroupValueMatchType, } from '../types/field';
import assertUnreachable from './assertUnreachable';
const createEscapeValue = (quoteChar, escapeChar) => {
    if (escapeChar === quoteChar) {
        return (value) => value.split(quoteChar).join(`${quoteChar}${quoteChar}`);
    }
    return (value) => value
        .split(escapeChar)
        .join(`${escapeChar}${escapeChar}`)
        .split(quoteChar)
        .join(`${escapeChar}${quoteChar}`);
};
export const getFixedWidthBinSelectSql = ({ binWidth, baseDimensionSql, warehouseSqlBuilder, }) => {
    const quoteChar = warehouseSqlBuilder.getStringQuoteChar();
    return `${warehouseSqlBuilder.concatString(`FLOOR(${baseDimensionSql} / ${binWidth}) * ${binWidth}`, `${quoteChar} - ${quoteChar}`, `(FLOOR(${baseDimensionSql} / ${binWidth}) + 1) * ${binWidth} - 1`)}`;
};
export const getCustomRangeSelectSql = ({ binRanges, baseDimensionSql, warehouseSqlBuilder, }) => {
    const quoteChar = warehouseSqlBuilder.getStringQuoteChar();
    const binRangeWhens = binRanges.map((range) => {
        if (range.from === undefined) {
            // First range
            return `WHEN ${baseDimensionSql} < ${range.to} THEN ${warehouseSqlBuilder.concatString(`${quoteChar}<${quoteChar}`, `${range.to}`)}`;
        }
        if (range.to === undefined) {
            // Last range
            return `ELSE ${warehouseSqlBuilder.concatString(`${quoteChar}≥${quoteChar}`, `${range.from}`)}`;
        }
        return `WHEN ${baseDimensionSql} >= ${range.from} AND ${baseDimensionSql} < ${range.to} THEN ${warehouseSqlBuilder.concatString(`${range.from}`, "'-'", `${range.to}`)}`;
    });
    // Add a NULL case for when the dimension is NULL, returning null as the value so it get's correctly formated with the symbol ∅
    const rangeWhens = [
        `WHEN ${baseDimensionSql} IS NULL THEN NULL`,
        ...binRangeWhens,
    ];
    return `CASE
            ${rangeWhens.join('\n')}
            END`;
};
const LIKE_ESCAPE_CHAR = '\\';
const escapeLikeWildcards = (value) => value
    .split(LIKE_ESCAPE_CHAR)
    .join(`${LIKE_ESCAPE_CHAR}${LIKE_ESCAPE_CHAR}`)
    .split('%')
    .join(`${LIKE_ESCAPE_CHAR}%`)
    .split('_')
    .join(`${LIKE_ESCAPE_CHAR}_`);
const renderValueCondition = (rule, baseDimensionSql, quoteChar, escapeValue) => {
    const escaped = escapeValue(rule.value);
    const likeEscape = `ESCAPE ${quoteChar}${escapeValue(LIKE_ESCAPE_CHAR)}${quoteChar}`;
    switch (rule.matchType) {
        case GroupValueMatchType.EXACT:
            return `${baseDimensionSql} = ${quoteChar}${escaped}${quoteChar}`;
        case GroupValueMatchType.STARTS_WITH: {
            const likeEscaped = escapeValue(escapeLikeWildcards(rule.value));
            return `${baseDimensionSql} LIKE ${quoteChar}${likeEscaped}%${quoteChar} ${likeEscape}`;
        }
        case GroupValueMatchType.ENDS_WITH: {
            const likeEscaped = escapeValue(escapeLikeWildcards(rule.value));
            return `${baseDimensionSql} LIKE ${quoteChar}%${likeEscaped}${quoteChar} ${likeEscape}`;
        }
        case GroupValueMatchType.INCLUDES: {
            const likeEscaped = escapeValue(escapeLikeWildcards(rule.value));
            return `${baseDimensionSql} LIKE ${quoteChar}%${likeEscaped}%${quoteChar} ${likeEscape}`;
        }
        default:
            return assertUnreachable(rule.matchType, `Unknown match type: ${rule.matchType}`);
    }
};
const renderGroupCondition = (rules, baseDimensionSql, quoteChar, escapeValue) => {
    // Optimise: batch exact-match rules into a single IN clause
    const exactRules = rules.filter((r) => r.matchType === GroupValueMatchType.EXACT);
    const patternRules = rules.filter((r) => r.matchType !== GroupValueMatchType.EXACT);
    const conditions = [];
    if (exactRules.length === 1) {
        conditions.push(renderValueCondition(exactRules[0], baseDimensionSql, quoteChar, escapeValue));
    }
    else if (exactRules.length > 1) {
        const inValues = exactRules
            .map((r) => `${quoteChar}${escapeValue(r.value)}${quoteChar}`)
            .join(', ');
        conditions.push(`${baseDimensionSql} IN (${inValues})`);
    }
    for (const rule of patternRules) {
        conditions.push(renderValueCondition(rule, baseDimensionSql, quoteChar, escapeValue));
    }
    if (conditions.length === 1) {
        return conditions[0];
    }
    return `(${conditions.join(' OR ')})`;
};
export const getCustomGroupSelectSql = ({ binGroups, baseDimensionSql, warehouseSqlBuilder, }) => {
    const quoteChar = warehouseSqlBuilder.getStringQuoteChar();
    const escapeChar = warehouseSqlBuilder.getEscapeStringQuoteChar();
    const escapeValue = createEscapeValue(quoteChar, escapeChar);
    const nonEmptyGroups = binGroups.filter((group) => group.values.length > 0);
    const groupWhens = nonEmptyGroups.map((group) => {
        const condition = renderGroupCondition(group.values, baseDimensionSql, quoteChar, escapeValue);
        return `WHEN ${condition} THEN ${quoteChar}${escapeValue(group.name)}${quoteChar}`;
    });
    const whens = [
        `WHEN ${baseDimensionSql} IS NULL THEN NULL`,
        ...groupWhens,
        `ELSE ${quoteChar}Other${quoteChar}`,
    ];
    return `CASE
            ${whens.join('\n')}
            END`;
};
export const getCustomGroupOrderSql = ({ binGroups, baseDimensionSql, warehouseSqlBuilder, }) => {
    const quoteChar = warehouseSqlBuilder.getStringQuoteChar();
    const escapeChar = warehouseSqlBuilder.getEscapeStringQuoteChar();
    const escapeValue = createEscapeValue(quoteChar, escapeChar);
    const nonEmptyGroups = binGroups.filter((group) => group.values.length > 0);
    const groupWhens = nonEmptyGroups.map((group, index) => {
        const condition = renderGroupCondition(group.values, baseDimensionSql, quoteChar, escapeValue);
        return `WHEN ${condition} THEN ${index}`;
    });
    const whens = [
        `WHEN ${baseDimensionSql} IS NULL THEN NULL`,
        ...groupWhens,
        `ELSE ${nonEmptyGroups.length}`,
    ];
    return `CASE
            ${whens.join('\n')}
            END`;
};
//# sourceMappingURL=customDimensions.js.map