import { groupBy, max, min } from '@antv/util';
import pdf from 'pdfast';
import { quantile } from '../../utils/transform/quantile';
import { ViolinOptions } from './types';

export type ViolinData = {
  /** X轴 */
  x: string;

  /** 小提琴轮廓的 size 通道数据 */
  violinSize: number[];
  /** 小提琴轮廓的 y 通道数据 */
  violinY: number[];

  // 箱线图基础数据
  /** 最大值 */
  high: number;
  /** 最小值 */
  low: number;
  /** 上四分位数 */
  q1: number;
  /** 下四分位数 */
  q3: number;
  /** 箱线图中的中位值 */
  median: number[];

  /** 箱线图中的上线边缘线 */
  minMax: number[];
  /** 箱线图中的上下四分位点 */
  quantile: number[];
};

export type PdfOptions = {
  min: number;
  max: number;
  size: number;
  width: number;
};

export const toBoxValue = (values: number[]) => {
  return {
    low: min(values),
    high: max(values),
    q1: quantile(values, 0.25),
    q3: quantile(values, 0.75),
    median: quantile(values, [0.5]),
    minMax: [min(values), max(values)],
    quantile: [quantile(values, 0.25), quantile(values, 0.75)],
  };
};

export const toViolinValue = (values: number[], pdfOptions: PdfOptions) => {
  const pdfResults: Array<{ x: number; y: number }> = pdf.create(values, pdfOptions);
  return {
    violinSize: pdfResults.map((result) => result.y),
    violinY: pdfResults.map((result) => result.x),
  };
};

export const transformViolinData = (options: ViolinOptions): ViolinData[] => {
  const { xField, yField, seriesField, data, kde } = options;

  /** 生成概率密度函数的配置 */
  const pdfOptions: PdfOptions = {
    min: kde.min,
    max: kde.max,
    size: kde.sampleSize,
    width: kde.width,
  };

  // 无拆分
  if (!seriesField) {
    const group = groupBy(data, xField);
    return Object.keys(group).map((x) => {
      const records = group[x];
      const values = records.map((record) => record[yField]);
      return {
        x,
        ...toViolinValue(values, pdfOptions),
        ...toBoxValue(values),
      };
    });
  }

  // 有拆分
  const resultList: ViolinData[] = [];
  const seriesGroup = groupBy(data, seriesField);
  Object.keys(seriesGroup).forEach((series) => {
    const group = groupBy(seriesGroup[series], xField);
    return Object.keys(group).forEach((key) => {
      const records = group[key];
      const values = records.map((record) => record[yField]);
      resultList.push({
        x: key,
        [seriesField]: series,
        ...toViolinValue(values, pdfOptions),
        ...toBoxValue(values),
      });
    });
  });
  return resultList;
};
