import type { JSONSupport, JSONSupportMixinProperties } from "../../core/JSONSupport.js";

export interface StatisticDefinitionProperties extends JSONSupportMixinProperties, Partial<Pick<StatisticDefinition, "onStatisticField" | "outStatisticFieldName" | "statisticParameters" | "statisticType">> {}

/**
 * This class defines the parameters for querying a layer or layer view for statistics.
 *
 * @since 4.0
 * @see [Query#outStatistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html#outStatistics)
 * @example
 * // query for the sum of the population in all features
 * let sumPopulation = {
 *   onStatisticField: "POP_2015",  // service field for 2015 population
 *   outStatisticFieldName: "Pop_2015_sum",
 *   statisticType: "sum"
 * }
 * let query = layer.createQuery();
 * query.outStatistics = [ sumPopulation ];
 * layer.queryFeatures(query)
 *   .then(function(response){
 *      let stats = response.features[0].attributes;
 *      console.log("output stats:", stats);
 *   });
 * @example
 * // query for the average of the population change for all features
 * let populationChangeDefinition = {
 *   onStatisticField: "POP_2015 - POP_2010",  // service field for 2015 population
 *   outStatisticFieldName: "avg_pop_change_2015_2010",
 *   statisticType: "avg"
 * }
 * let query = layer.createQuery();
 * query.outStatistics = [ populationChangeDefinition ];
 * layer.queryFeatures(query)
 *   .then(function(response){
 *      let stats = response.features[0].attributes;
 *      console.log("Average change:", stats.avg_pop_change_2015_2010);
 *   });
 */
export default class StatisticDefinition extends JSONSupport {
  constructor(properties?: StatisticDefinitionProperties);
  /**
   * Defines the field for which statistics will be calculated. This can be service field names
   * or SQL expressions. See the snippets below for examples.
   *
   * @example
   * // query for the sum of the population in all features
   * let sumPopulation = {
   *   onStatisticField: "POP_2015",  // service field for 2015 population
   *   outStatisticFieldName: "Pop_2015_sum",
   *   statisticType: "sum"
   * }
   * let query = layer.createQuery();
   * query.outStatistics = [ sumPopulation ];
   * layer.queryFeatures(query)
   *   .then(function(response){
   *      let stats = response.features[0].attributes;
   *      console.log("output stats:", stats);
   *   });
   * @example
   * // query for the average of the population change for all features
   * // Notice that you can pass a SQL expression as a field name to calculate statistics
   * let populationChangeDefinition = {
   *   onStatisticField: "POP_2015 - POP_2010",  // service field for 2015 population
   *   outStatisticFieldName: "avg_pop_change_2015_2010",
   *   statisticType: "avg"
   * }
   * let query = layer.createQuery();
   * query.outStatistics = [ populationChangeDefinition ];
   * layer.queryFeatures(query)
   *   .then(function(response){
   *      let stats = response.features[0].attributes;
   *      console.log("Average change:", stats.avg_pop_change_2015_2010);
   *   });
   * @example
   * // query for the average of the population change grouped by regions
   * // query result will also return an extent for each group encompassing
   * // all features in each group.
   * let populationChangeDefinition = {
   *   onStatisticField: "POP_2015 - POP_2010",  // service field for 2015 population
   *   outStatisticFieldName: "avg_pop_change_2015_2010",
   *   statisticType: "avg"
   * };
   * let aggregatedExtent = {
   *   statisticType: "envelope-aggregate"
   * };
   * let query = layer.createQuery();
   * query.groupByFieldsForStatistics = ["Region"];
   * query.outStatistics = [ populationChangeDefinition, aggregatedExtent ];
   * layer.queryFeatures(query).then(displayResults);
   */
  accessor onStatisticField: string | null | undefined;
  /**
   * Specifies the output field name for the requested statistic. Output field names can only contain alpha-numeric characters and an underscore. If no output
   * field name is specified, the server assigns a field name to the returned statistic field.
   */
  accessor outStatisticFieldName: string | null | undefined;
  /**
   * The parameters for [percentile statistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-StatisticDefinition.html#statisticType). This property must be set when the [statisticType](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-StatisticDefinition.html#statisticType) is set to either `percentile-continuous` or `percentile-discrete`.
   *
   * @example
   * let query = layer.createQuery();
   * // find the median value in descending order for response_rate field
   * // for all features stored in the layer and order
   * query.outStatistics = [{
   *   statisticType: "percentile-continuous",
   *   statisticParameters: {
   *     value: 0.5,
   *     orderBy: "DESC"
   *   },
   *   onStatisticField: "response_rate",
   *   outStatisticFieldName: "Resp_rate_median"
   * }];
   * // query the features for the median value statistics against the values
   * // stored in the response_rate field
   * queryFeatures(query);
   * @example
   * // Query the percentile for response time in descending order for all features in the layer
   * // group the percentile by Division and unit fields
   * let query = layer.createQuery();
   * query.orderByFields = ["Division, Unit"];
   * query.groupByFieldsForStatistics = ["Division, Unit"];
   * query.outStatistics = [{
   *   statisticType: "percentile-discrete",
   *   statisticParameters: {
   *     value: 0.67,
   *     orderBy: "DESC"
   *   },
   *   onStatisticField: "response_time",
   *   outStatisticFieldName: "response_time_percentile"
   * }];
   * queryFeatures(query);
   */
  accessor statisticParameters: PercentileParameters | null | undefined;
  /**
   * Defines the type of statistic.
   *
   * **Possible Values**
   *
   * Value | Description |
   * ----- | ----------- |
   * count | The number of features that match a specified criteria.
   * sum | The total sum of values that match a specified criteria.
   * min | The minimum value of a given field.
   * max | The maximum value of a given field.
   * avg | The average of values that match a specified criteria.
   * stddev | The standard deviation of values that match a specified criteria.
   * var | The statistical variance of values in the specified criteria.
   * percentile-continuous | An interpolated value above or below which a given percentage of values in a group of data lie. For example, the 90th percentile (value 0.9) is the value below which 90% of the data values may be found. `percentile-continuous` returns an interpolated value from the dataset.
   * percentile-discrete | Similar to `percentile-continuous` except `percentile-discrete` returns a data value from a dataset.
   * envelope-aggregate | Returns the spatial extent of grouped features when  [Query#groupByFieldsForStatistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html#groupByFieldsForStatistics) is used. Each statistics group will have an extent representing the bounding box of all features in that group.
   * centroid-aggregate | Returns the centroid of the grouped features when [Query#groupByFieldsForStatistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html#groupByFieldsForStatistics) is used. Each statistics group will have a centroid representing the spatial center of features belonging to the group.
   * convex-hull-aggregate | Returns the convex hull of grouped features when [Query#groupByFieldsForStatistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html#groupByFieldsForStatistics) is used. Each statistics group will have a convex hull representing the smallest area containing all features in that group.
   *
   * > [!WARNING]
   * >
   * > **Known Limitations**
   * >
   * > The [statisticParameters](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-StatisticDefinition.html#statisticParameters) must be set when calculating `percentile-continuous` or `percentile-discrete` statistics.
   * > The `percentile-continuous` and `percentile-discrete` statistic types cannot be used with the
   * > [Query#having](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-support-Query.html#having) parameter.
   * > The `percentile-continuous` and `percentile-discrete` statistic types are supported if [capabilities.query.supportsPercentileStatistics](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#capabilities) is `true`.
   * > The `envelope-aggregate`, `centroid-aggregate` and `convex-hull-aggregate` statistic types are not supported with ArcGIS Enterprise hosted and non-hosted feature services.
   *
   * @see [Graphic#aggregateGeometries](https://developers.arcgis.com/javascript/latest/api-reference/esri-Graphic.html#aggregateGeometries)
   * @example
   * // average of age fields by regions
   * const ageStatsByRegion = new StatisticDefinition({
   *   onStatisticField: field,
   *   outStatisticFieldName: "avgAge",
   *   statisticType: "avg"
   * });
   *
   * // extent encompassing all features by region
   * const aggregatedExtent = new StatisticDefinition({
   *   statisticType: "envelope-aggregate",
   *   outStatisticFieldName: "aggregateExtent",
   * });
   *
   * // group the statistics by Region field
   * // get avg age by Regions and extent of each region
   * const query = layer.createQuery();
   * query.groupByFieldsForStatistics = ["Region"];
   * query.outStatistics = [consumeStatsByRegion, aggregatedExtent];
   * layer.queryFeatures(query).then((results) => {
   *   results.features.forEach((feature) => {
   *     if (feature.attributes.Region === "Midwest") {
   *        view.goTo(feature.aggregateGeometries.aggregateExtent);
   *     }
   *   });
   * });
   */
  accessor statisticType: "count" | "sum" | "min" | "max" | "avg" | "stddev" | "var" | "exceedslimit" | "percentile-continuous" | "percentile-discrete" | "envelope-aggregate" | "centroid-aggregate" | "convex-hull-aggregate" | null | undefined;
  /**
   * Creates a deep clone of StatisticDefinition object.
   *
   * @returns A new instance of a StatisticDefinition object equal to the object used to call `.clone()`.
   */
  clone(): StatisticDefinition;
}

export interface PercentileParameters {
  /**
   * Specify `ASC` (ascending) or `DESC` (descending) to control the order of the data.
   * For example, in a data set of 10 values from 1 to 10, the percentile value for 0.9 with `orderBy` set to ascending (`ASC`) is 9, but
   * when `orderBy` is set to descending (`DESC`) the result is 2. The default is `ASC`.
   */
  orderBy?: "ASC" | "DESC";
  /** Percentile value. This should be a decimal value between 0 and 1. */
  value: number;
}