text: /*
   * Licensed to Elasticsearch B.V. under one or more contributor
   * license agreements. See the NOTICE file distributed with
   * this work for additional information regarding copyright
   * ownership. Elasticsearch B.V. licenses this file to you under
   * the Apache License, Version 2.0 (the "License"); you may
   * not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  import _ from 'lodash';
  import { MetricAggType } from './metric_agg_type';
  import topSortEditor from '../controls/top_sort.html';
  import aggregateAndSizeEditor from '../controls/top_aggregate_and_size.html';
  import { aggTypeFieldFilters } from '../param_types/filter';
  
  const isNumber = function (type) {
    return type === 'number';
  };
  
  aggTypeFieldFilters.addFilter(
    (
      field,
      fieldParamType,
      aggConfig,
      vis
    ) => {
      if (aggConfig.type.name !== 'top_hit' || vis.type.name === 'table' || vis.type.name === 'metric') {
        return true;
      }
      return field.type === 'number';
  
    });
  
  export const topHitMetricAgg = new MetricAggType({
    name: 'top_hits',
    title: 'Top Hit',
    makeLabel: function (aggConfig) {
      let prefix = aggConfig.params.sortOrder.val === 'desc' ? 'Last' : 'First';
      if (aggConfig.params.size !== 1) {
        prefix += ` ${aggConfig.params.size}`;
      }
      const field = aggConfig.params.field;
      return `${prefix} ${field ? field.displayName : ''}`;
    },
    params: [
      {
        name: 'field',
        type: 'field',
        onlyAggregatable: false,
        filterFieldTypes: '*',
        write(agg, output) {
          const field = agg.params.field;
          output.params = {};
  
          if (field.scripted) {
            output.params.script_fields = {
              [ field.name ]: {
                script: {
                  inline: field.script,
                  lang: field.lang
                }
              }
            };
          } else {
            if (field.readFromDocValues) {
              output.params.docvalue_fields = [ { field: field.name, format: 'use_field_mapping' } ];
            }
            output.params._source = field.name === '_source' ? true : field.name;
          }
        }
      },
      {
        name: 'aggregate',
        type: 'optioned',
        editor: aggregateAndSizeEditor,
        options: [
          {
            display: 'Min',
            isCompatibleType: isNumber,
            isCompatibleVis: _.constant(true),
            disabled: true,
            val: 'min'
          },
          {
            display: 'Max',
            isCompatibleType: isNumber,
            isCompatibleVis: _.constant(true),
            disabled: true,
            val: 'max'
          },
          {
            display: 'Sum',
            isCompatibleType: isNumber,
            isCompatibleVis: _.constant(true),
            disabled: true,
            val: 'sum'
          },
          {
            display: 'Average',
            isCompatibleType: isNumber,
            isCompatibleVis: _.constant(true),
            disabled: true,
            val: 'average'
          },
          {
            display: 'Concatenate',
            isCompatibleType: _.constant(true),
            isCompatibleVis: function (name) {
              return name === 'metric' || name === 'table';
            },
            disabled: true,
            val: 'concat'
          }
        ],
        controller: function ($scope) {
          $scope.options = [];
          $scope.$watchGroup([ 'vis.type.name', 'agg.params.field.type' ], function ([ visName, fieldType ]) {
            if (fieldType && visName) {
              $scope.options = _.filter($scope.aggParam.options, option => {
                return option.isCompatibleVis(visName) && option.isCompatibleType(fieldType);
              });
              if ($scope.options.length === 1) {
                $scope.agg.params.aggregate = $scope.options[0];
              }
            }
          });
        },
        write: _.noop
      },
      {
        name: 'size',
        editor: null, // size setting is done together with the aggregation setting
        default: 1
      },
      {
        name: 'sortField',
        type: 'field',
        editor: null,
        filterFieldTypes: [ 'number', 'date', 'ip',  'string' ],
        default: function (agg) {
          return agg.getIndexPattern().timeFieldName;
        },
        write: _.noop // prevent default write, it is handled below
      },
      {
        name: 'sortOrder',
        type: 'optioned',
        default: 'desc',
        editor: topSortEditor,
        options: [
          { display: 'Descending', val: 'desc' },
          { display: 'Ascending', val: 'asc' }
        ],
        write(agg, output) {
          const sortField = agg.params.sortField;
          const sortOrder = agg.params.sortOrder;
  
          if (sortField.scripted) {
            output.params.sort = [
              {
                _script: {
                  script: {
                    inline: sortField.script,
                    lang: sortField.lang
                  },
                  type: sortField.type,
                  order: sortOrder.val
                }
              }
            ];
          } else {
            output.params.sort = [
              {
                [ sortField.name ]: {
                  order: sortOrder.val
                }
              }
            ];
          }
        }
      }
    ],
    getValue(agg, bucket) {
      const hits = _.get(bucket, `${agg.id}.hits.hits`);
      if (!hits || !hits.length) {
        return null;
      }
      const path = agg.params.field.name;
  
      let values = _(hits).map(hit => {
        return path === '_source' ? hit._source : agg.getIndexPattern().flattenHit(hit, true)[path];
      })
        .flatten()
        .value();
  
      if (values.length === 1) {
        values = values[0];
      }
  
      if (Array.isArray(values)) {
        if (!_.compact(values).length) {
          return null;
        }
        switch (agg.params.aggregate.val) {
          case 'max':
            return _.max(values);
          case 'min':
            return _.min(values);
          case 'sum':
            return _.sum(values);
          case 'average':
            return _.sum(values) / values.length;
        }
      }
      return values;
    }
  });
  