import { isNullish } from "remeda";
import { array, clamper, regressionLinear, regressionLogistic, regressionLowess, regressionPolynomial } from "@visactor/vutils";
import { getAlphaByConfidenceLevel } from "./common.js";
const generateRegressionLinePipe = (type, regressionFunction, getOptions = getDefaultRegressionOptions, getMinPoints = ()=>2)=>(spec, context)=>{
        const result = {
            ...spec
        };
        const { advancedVSeed } = context;
        const { chartType, regressionLine } = advancedVSeed;
        const lineTheme = advancedVSeed.config[chartType]?.regressionLine;
        if (!regressionLine || !regressionLine[type]) return result;
        const lineList = array(regressionLine[type]);
        if (!result.extensionMark) result.extensionMark = [];
        lineList.forEach((line, lineIndex)=>{
            if (false === line.enable) return;
            const theme = lineTheme.linearRegressionLine ?? {};
            const { color, lineWidth, lineDash, text, textColor, textFontSize, textFontWeight, confidenceIntervalOpacity, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, confidenceIntervalVisible = theme.confidenceIntervalVisible } = line;
            const childrenMarks = [];
            result.extensionMark.push({
                type: 'group',
                interactive: false,
                zIndex: 500,
                name: `${type}-${lineIndex}`,
                layoutType: 'region-relative',
                dataId: spec.data?.id,
                animation: false,
                style: {
                    data: (datum, ctx)=>{
                        const vchart = ctx.vchart;
                        const chart = vchart.getChart();
                        const s = chart.getAllSeries()[0];
                        if (s) {
                            const rect = s.getRegion().getLayoutRect();
                            const segments = [];
                            if (0 === rect.width || 0 === rect.height) return segments;
                            const yClamper = clamper(0, rect.height);
                            const colorAttrOptions = s.getColorAttribute();
                            const groups = s.getSeriesKeys();
                            const data = s.getViewData()?.latestData;
                            const fieldX = s.fieldX?.[0];
                            const fieldY = s.fieldY?.[0];
                            if (!groups.length) groups.push(void 0);
                            groups.forEach((group)=>{
                                const groupData = data.filter((d)=>d[colorAttrOptions?.field] === group);
                                const minPoints = getMinPoints(line);
                                if (groupData.length < minPoints) return;
                                const { confidenceInterval, evaluateGrid } = regressionFunction(groupData, (datum)=>datum?.[fieldX], (datum)=>datum?.[fieldY], getOptions?.(line));
                                const N = Math.max(3, Math.floor(groupData.length / 4));
                                const mainColor = color ?? colorAttrOptions?.scale?.scale(group);
                                const lineData = evaluateGrid(N);
                                const linePoints = [];
                                lineData.forEach((ld, index)=>{
                                    const d = {
                                        [fieldX]: ld.x,
                                        [fieldY]: ld.y
                                    };
                                    const x = s.dataToPositionX(d);
                                    const y = yClamper(s.dataToPositionY(d));
                                    if (segments.length && 0 === index) segments[segments.length - 1].linePoints.push({
                                        x,
                                        y: NaN
                                    });
                                    linePoints.push({
                                        x,
                                        y
                                    });
                                });
                                const segment = {
                                    color: mainColor,
                                    linePoints
                                };
                                if (confidenceIntervalVisible) {
                                    const intervalData = confidenceInterval(N);
                                    const areaPoints = [];
                                    intervalData.map((datum, index)=>{
                                        const d = {
                                            [fieldX]: datum.x,
                                            [fieldY]: datum.lower
                                        };
                                        const x = s.dataToPositionX(d);
                                        const y = yClamper(s.dataToPositionY(d));
                                        const y1 = yClamper(s.dataToPositionY({
                                            [fieldY]: datum.upper
                                        }));
                                        if (segments.length && 0 === index) segments[segments.length - 1].areaPoints.push({
                                            x,
                                            y: NaN,
                                            y1: NaN
                                        });
                                        areaPoints.push({
                                            x,
                                            y,
                                            y1
                                        });
                                    });
                                    segment.areaPoints = areaPoints;
                                }
                                segments.push(segment);
                            });
                            return segments;
                        }
                        return [];
                    }
                },
                children: childrenMarks
            });
            if (confidenceIntervalVisible) childrenMarks.push({
                type: 'area',
                interactive: false,
                zIndex: 500,
                dataId: spec.data?.id,
                style: {
                    stroke: false,
                    lineWidth: lineWidth ?? theme.lineWidth,
                    lineDash: lineDash ?? theme.lineDash,
                    fillOpacity: confidenceIntervalOpacity ?? theme.confidenceIntervalOpacity,
                    fill: 'red',
                    segments: (datum, ctx, opt)=>{
                        const parentNode = opt.mark?._product?.parent;
                        if (parentNode) {
                            const data = parentNode.finalAttribute?.data ?? parentNode.attribute?.data;
                            if (data?.length) return data.map((d)=>({
                                    points: d.areaPoints ?? [],
                                    fill: d.color
                                }));
                        }
                        return [];
                    }
                }
            });
            childrenMarks.push({
                type: 'line',
                interactive: false,
                zIndex: 500,
                animation: false,
                dataId: spec.data?.id,
                style: {
                    lineWidth: lineWidth ?? theme.lineWidth,
                    lineDash: lineDash ?? theme.lineDash,
                    stroke: 'red',
                    shadowBlur: shadowBlur ?? theme.shadowBlur,
                    shadowColor: shadowColor ?? theme.shadowColor,
                    shadowOffsetX: shadowOffsetX ?? theme.shadowOffsetX,
                    shadowOffsetY: shadowOffsetY ?? theme.shadowOffsetY,
                    segments: (datum, ctx, opt)=>{
                        const parentNode = opt.mark?._product?.parent;
                        if (parentNode) {
                            const data = parentNode.finalAttribute?.data ?? parentNode.attribute?.data;
                            if (data?.length) return data.map((d)=>({
                                    points: d.linePoints,
                                    stroke: d.color
                                }));
                        }
                        return [];
                    }
                }
            });
            if (!isNullish(text)) childrenMarks.push({
                type: 'text',
                interactive: false,
                zIndex: 500,
                animation: false,
                dataId: spec.data?.id,
                dataKey: ()=>`regressionLine-label-${lineIndex}`,
                style: {
                    textAlign: 'end',
                    fill: textColor ?? theme.textColor,
                    fontSize: textFontSize ?? theme.textFontSize,
                    fontWeight: textFontWeight ?? theme.textFontWeight,
                    text: text,
                    x: (datum, ctx, opt)=>{
                        const parentNode = opt.mark?._product?.parent;
                        if (parentNode?.attribute?.data?.length) {
                            const point = parentNode.attribute.data[parentNode.attribute.data.length - 1].linePoints;
                            return point[point.length - 1]?.x;
                        }
                    },
                    y: (datum, ctx, opt)=>{
                        const parentNode = opt.mark?._product?.parent;
                        if (parentNode?.attribute?.data?.length) {
                            const point = parentNode.attribute.data[parentNode.attribute.data.length - 1].linePoints;
                            return point[point.length - 1]?.y;
                        }
                    }
                }
            });
        });
        return result;
    };
const getDefaultRegressionOptions = (lineConfig)=>{
    const alpha = getAlphaByConfidenceLevel(lineConfig?.confidenceLevel);
    return {
        alpha
    };
};
const linearRegressionLine = generateRegressionLinePipe('linearRegressionLine', regressionLinear, getDefaultRegressionOptions);
const lowessRegressionLine = generateRegressionLinePipe('lowessRegressionLine', regressionLowess, getDefaultRegressionOptions);
const polynomialRegressionLine = generateRegressionLinePipe('polynomialRegressionLine', regressionPolynomial, (lineConfig)=>({
        ...getDefaultRegressionOptions(lineConfig),
        degree: lineConfig.degree ?? 2
    }), (lineConfig)=>(lineConfig.degree ?? 2) + 1);
const logisticRegressionLine = generateRegressionLinePipe('logisticRegressionLine', regressionLogistic, getDefaultRegressionOptions);
export { generateRegressionLinePipe, linearRegressionLine, logisticRegressionLine, lowessRegressionLine, polynomialRegressionLine };

//# sourceMappingURL=linearRegressionLine.js.map