import { getFirstIndexColumns } from '../pivot/utils';
import { DimensionType } from '../types/field';
import { PieChartTooltipLabelMaxLength, } from '../types/savedCharts';
import { applyCustomFormat } from '../utils/formatting';
import { getLegendStyle } from './helpers/styles/legendStyles';
import { formatColorIndicator, formatTooltipLabel, formatTooltipRow, formatTooltipValue, getTooltipStyle, } from './helpers/styles/tooltipStyles';
import { VizAggregationOptions, VizIndexType, } from './types';
const defaultFieldConfig = {
    x: {
        reference: 'x',
        type: VizIndexType.CATEGORY,
    },
    y: [
        {
            reference: 'y',
            aggregation: VizAggregationOptions.SUM,
        },
    ],
    groupBy: [],
};
export class PieChartDataModel {
    constructor(args) {
        this.resultsRunner = args.resultsRunner;
        this.fieldConfig = args.fieldConfig ?? defaultFieldConfig;
    }
    getDefaultLayout() {
        const { groupFieldOptions, metricFieldOptions, customMetricFieldOptions, } = this.getResultOptions();
        const categoricalFields = groupFieldOptions.filter((field) => field.dimensionType === DimensionType.STRING);
        const booleanFields = groupFieldOptions.filter((field) => field.dimensionType === DimensionType.BOOLEAN);
        const dateFields = groupFieldOptions.filter((field) => field.dimensionType &&
            [DimensionType.DATE, DimensionType.TIMESTAMP].includes(field.dimensionType));
        const numericFields = groupFieldOptions.filter((field) => field.dimensionType === DimensionType.NUMBER);
        const groupField = categoricalFields[0] ||
            booleanFields[0] ||
            dateFields[0] ||
            numericFields[0];
        if (groupField === undefined) {
            return undefined;
        }
        const group = {
            reference: groupField.reference,
            type: groupField.axisType,
        };
        const metricFields = [
            ...metricFieldOptions,
            ...customMetricFieldOptions,
        ];
        const metricField = metricFields.filter((field) => field.reference !== group.reference)[0];
        if (metricField === undefined) {
            return undefined;
        }
        // TODO: this should have its own type so it doesnt reference x and y
        return {
            x: group,
            y: [
                {
                    reference: metricField.reference,
                    aggregation: metricField.aggregation,
                },
            ],
            groupBy: [],
        };
    }
    mergeConfig(chartKind, currentVizConfig) {
        const newDefaultLayout = this.getDefaultLayout();
        const mergedLayout = newDefaultLayout?.x?.reference ===
            currentVizConfig?.fieldConfig?.x?.reference
            ? currentVizConfig?.fieldConfig
            : newDefaultLayout;
        return {
            metadata: {
                version: 1,
            },
            type: chartKind,
            fieldConfig: mergedLayout ?? currentVizConfig?.fieldConfig,
            display: currentVizConfig?.display ?? {},
        };
    }
    getResultOptions() {
        return {
            groupFieldOptions: this.resultsRunner.getPivotQueryDimensions(),
            metricFieldOptions: this.resultsRunner.getPivotQueryMetrics(),
            customMetricFieldOptions: this.resultsRunner.getPivotQueryCustomMetrics(),
        };
    }
    getConfigErrors(config) {
        if (!config) {
            return undefined;
        }
        const { groupFieldOptions, metricFieldOptions, customMetricFieldOptions, } = this.getResultOptions();
        const indexFieldError = Boolean(config?.x?.reference &&
            groupFieldOptions.find((x) => x.reference === config?.x?.reference) === undefined);
        const metricFieldError = Boolean(config?.y?.some((yField) => yField.reference &&
            metricFieldOptions.find((y) => y.reference === yField.reference) === undefined));
        const customMetricFieldError = Boolean(config?.y?.some((yField) => yField.reference &&
            customMetricFieldOptions.find((y) => y.reference === yField.reference) === undefined));
        if (!indexFieldError && !metricFieldError && !customMetricFieldError) {
            return undefined;
        }
        return {
            ...(indexFieldError &&
                config?.x?.reference && {
                indexFieldError: {
                    reference: config.x.reference,
                },
            }),
            // TODO: can we combine metricFieldError and customMetricFieldError?
            // And maybe take out some noise
            ...(metricFieldError &&
                config?.y?.map((y) => y.reference) && {
                metricFieldError: {
                    references: config?.y.reduce((acc, y) => {
                        const valuesLayoutOption = metricFieldOptions.find((v) => v.reference === y.reference);
                        if (!valuesLayoutOption) {
                            acc.push(y.reference);
                        }
                        return acc;
                    }, []),
                },
            }),
            ...(customMetricFieldError &&
                config?.y?.map((y) => y.reference) && {
                customMetricFieldError: {
                    references: config?.y.reduce((acc, y) => {
                        const valuesLayoutOption = customMetricFieldOptions.find((v) => v.reference === y.reference);
                        if (!valuesLayoutOption) {
                            acc.push(y.reference);
                        }
                        return acc;
                    }, []),
                },
            }),
        };
    }
    async getTransformedData(query) {
        if (!query) {
            return undefined;
        }
        return this.resultsRunner.getPivotedVisualizationData(query);
    }
    // TODO: dupe function code - see cartesian chart
    async getPivotedChartData({ sortBy, filters, limit, sql, }) {
        const allDimensionNames = new Set(this.resultsRunner
            .getPivotQueryDimensions()
            .map((d) => d.reference));
        const allSelectedDimensions = [
            this.fieldConfig.x?.reference,
            ...(this.fieldConfig.groupBy?.map((groupBy) => groupBy.reference) ??
                []),
        ];
        const [timeDimensions, dimensions] = this.resultsRunner
            .getPivotQueryDimensions()
            .reduce((acc, dimension) => {
            if (allSelectedDimensions.includes(dimension.reference)) {
                if (dimension.dimensionType === DimensionType.DATE ||
                    dimension.dimensionType === DimensionType.TIMESTAMP) {
                    acc[0].push({ name: dimension.reference });
                }
                else {
                    acc[1].push({ name: dimension.reference });
                }
            }
            return acc;
        }, [[], []]);
        const { customMetrics, metrics } = this.fieldConfig.y?.reduce((acc, field) => {
            if (allDimensionNames.has(field.reference)) {
                // it's a custom metric
                const name = `${field.reference}_${field.aggregation}`;
                acc.customMetrics.push({
                    name,
                    baseDimension: field.reference,
                    aggType: field.aggregation,
                });
                acc.metrics.push({ name });
            }
            else {
                acc.metrics.push({ name: field.reference });
            }
            return acc;
        }, { customMetrics: [], metrics: [] }) ?? { customMetrics: [], metrics: [] };
        const pivot = {
            index: this.fieldConfig.x?.reference
                ? [this.fieldConfig.x?.reference]
                : [],
            on: this.fieldConfig.groupBy?.map((groupBy) => groupBy.reference) ??
                [],
            values: metrics.map((metric) => metric.name),
        };
        const query = {
            sql,
            limit,
            filters,
            sortBy,
            metrics,
            dimensions,
            timeDimensions,
            pivot,
            customMetrics,
        };
        const pivotedChartData = await this.getTransformedData(query);
        this.pivotedChartData = pivotedChartData;
        return pivotedChartData;
    }
    getPivotedTableData() {
        const transformedData = this.pivotedChartData;
        if (!transformedData) {
            return undefined;
        }
        return {
            columns: Object.keys(transformedData.results[0]) ?? [],
            rows: transformedData.results,
        };
    }
    getDataDownloadUrl() {
        const transformedData = this.pivotedChartData;
        if (!transformedData) {
            return undefined;
        }
        return transformedData.fileUrl;
    }
    getSpec(display) {
        const transformedData = this.pivotedChartData;
        if (!transformedData) {
            return {};
        }
        const spec = {
            legend: {
                show: true,
                orient: 'horizontal',
                type: 'scroll',
                left: 'center',
                top: 'top',
                align: 'auto',
                ...getLegendStyle('square'),
            },
            tooltip: {
                trigger: 'item',
                ...getTooltipStyle(),
                formatter: (params) => {
                    // Safely extract properties from params
                    const { color, name: nameValue, value, percent } = params;
                    const formattedValue = typeof value === 'number'
                        ? applyCustomFormat(value, undefined)
                        : String(value ?? '');
                    // Truncate long names
                    const nameStr = String(nameValue ?? '');
                    const truncatedName = nameStr.length > PieChartTooltipLabelMaxLength
                        ? `${nameStr.slice(0, PieChartTooltipLabelMaxLength)}...`
                        : nameStr;
                    const colorIndicator = formatColorIndicator(typeof color === 'string' ? color : '#000');
                    const label = formatTooltipLabel(truncatedName);
                    const valueWithPercent = `${percent}% - ${formattedValue}`;
                    const valuePill = formatTooltipValue(valueWithPercent);
                    return formatTooltipRow(colorIndicator, label, valuePill);
                },
            },
            series: [
                {
                    type: 'pie',
                    radius: display?.isDonut ? ['30%', '70%'] : '50%',
                    center: ['50%', '50%'],
                    data: transformedData.results.map((result) => {
                        const firstIndexColumn = getFirstIndexColumns(transformedData.indexColumn);
                        return {
                            name: firstIndexColumn?.reference
                                ? result[firstIndexColumn.reference]
                                : '-',
                            groupId: firstIndexColumn?.reference,
                            // Pie chart uses only the first value column, though others
                            // could be returned from the pivot query. If the pie chart
                            // ever supports pivoting, we'll need to update this.
                            value: result[transformedData.valuesColumns[0].pivotColumnName],
                        };
                    }),
                },
            ],
            textStyle: {
                fontFamily: 'Inter, sans-serif',
            },
        };
        return spec;
    }
}
//# sourceMappingURL=PieChartDataModel.js.map