import type { NumberArray } from 'cheminfo-types';

import { xBoxPlot } from './xBoxPlot.ts';

export interface XBoxPlotWithOutliers {
  /**
   * The minimum value of the number array
   */
  min: number;
  /**
   * q1 - 1.5 * IQR
   */
  lowerWhisker: number;
  /**
   * The minimum value of the number array that is not an outlier
   */
  minWhisker: number;
  /**
   * The first quartile of the number array
   */
  q1: number;
  /**
   * The median of the number array
   */
  median: number;
  /**
   * The third quartile of the number array
   */
  q3: number;
  /**
   * q3 + 1.5 * IQR
   */
  maxWhisker: number;
  /**
   * The maximal value of the number array that is not an outlier
   */
  upperWhisker: number;
  /**
   * The maximum value of the number array
   */
  max: number;
  /**
   * q3 - q1
   */
  iqr: number;
  /**
   * The outliers of the number array based on 1.5 * IQR
   */
  outliers: number[];
}

/**
 * Calculating the box plot of the array with outliers
 * Values are outliers if they are below Q1 - 1.5 * IQR or above Q3 + 1.5 * IQR
 * @param array - data
 * @returns - q1, median, q3, min, max, outliers
 */
export function xBoxPlotWithOutliers(array: NumberArray): XBoxPlotWithOutliers {
  const boxPlot = xBoxPlot(array);

  if (boxPlot.max - boxPlot.min <= Number.EPSILON) {
    return {
      ...boxPlot,
      lowerWhisker: boxPlot.min,
      upperWhisker: boxPlot.max,
      minWhisker: boxPlot.min,
      maxWhisker: boxPlot.max,
      iqr: 0,
      outliers: [],
    };
  }

  const iqr = boxPlot.q3 - boxPlot.q1;
  const lowerWhisker = boxPlot.q1 - 1.5 * iqr;
  const upperWhisker = boxPlot.q3 + 1.5 * iqr;

  const outliers = [];
  let minWhisker = boxPlot.median;
  let maxWhisker = boxPlot.median;
  for (const value of array) {
    if (value < lowerWhisker || value > upperWhisker) {
      outliers.push(value);
    } else {
      if (value < minWhisker) minWhisker = value;
      if (value > maxWhisker) maxWhisker = value;
    }
  }

  const info: XBoxPlotWithOutliers = {
    ...boxPlot,
    lowerWhisker,
    upperWhisker,
    minWhisker,
    maxWhisker,
    iqr,
    outliers,
  };

  return info;
}
