import { IndexedArray } from 'ui/indexed_array';
import _ from 'lodash';
import $ from 'jquery';
import aggSelectHtml from 'plugins/kibana/visualize/editor/agg_select.html';
import advancedToggleHtml from 'plugins/kibana/visualize/editor/advanced_toggle.html';
import 'ui/filters/match_any';
import 'plugins/kibana/visualize/editor/agg_param';
import { AggTypesIndexProvider } from 'ui/agg_types/index';
import { uiModules } from 'ui/modules';
import aggParamsTemplate from 'plugins/kibana/visualize/editor/agg_params.html';

uiModules
.get('app/visualize')
.directive('visEditorAggParams', function ($compile, $parse, Private, Notifier, $filter) {
  const aggTypes = Private(AggTypesIndexProvider);

  return {
    restrict: 'E',
    template: aggParamsTemplate,
    scope: true,
    link: function ($scope, $el, attr) {
      $scope.$bind('agg', attr.agg);
      $scope.$bind('groupName', attr.groupName);

      $scope.aggTypeOptions = aggTypes.byType[$scope.groupName];
      $scope.advancedToggled = false;

      // We set up this watch prior to adding the controls below, because when the controls are added,
      // there is a possibility that the agg type can be automatically selected (if there is only one)
      $scope.$watch('agg.type', updateAggParamEditor);

      // this will contain the controls for the schema (rows or columns?), which are unrelated to
      // controls for the agg, which is why they are first
      addSchemaEditor();

      // allow selection of an aggregation
      addAggSelector();

      function addSchemaEditor() {
        const $schemaEditor = $('<div>').addClass('schemaEditors').appendTo($el);

        if ($scope.agg.schema.editor) {
          $schemaEditor.append($scope.agg.schema.editor);
          $compile($schemaEditor)($scope.$new());
        }
      }

      function addAggSelector() {
        const $aggSelect = $(aggSelectHtml).appendTo($el);
        $compile($aggSelect)($scope);
      }

      // params for the selected agg, these are rebuilt every time the agg in $aggSelect changes
      let $aggParamEditors; //  container for agg type param editors
      let $aggParamEditorsScope;

      function updateAggParamEditor(newType, oldType) {
        if ($aggParamEditors) {
          $aggParamEditors.remove();
          $aggParamEditors = null;
        }

        // if there's an old scope, destroy it
        if ($aggParamEditorsScope) {
          $aggParamEditorsScope.$destroy();
          $aggParamEditorsScope = null;
        }

        // create child scope, used in the editors
        $aggParamEditorsScope = $scope.$new();
        $aggParamEditorsScope.indexedFields = $scope.agg.getFieldOptions();

        const agg = $scope.agg;
        if (!agg) return;

        const type = $scope.agg.type;

        if (newType !== oldType) {
          // don't reset on initial load, the
          // saved params should persist
          agg.resetParams();
        }

        if (!type) return;

        const aggParamHTML = {
          basic: [],
          advanced: []
        };

        // build collection of agg params html
        type.params.forEach(function (param, i) {
          let aggParam;
          let fields;
          if (agg.schema.hideCustomLabel && param.name === 'customLabel') {
            return;
          }
          // if field param exists, compute allowed fields
          if (param.name === 'field') {
            fields = $aggParamEditorsScope.indexedFields;
          } else if (param.type === 'field') {
            fields = $aggParamEditorsScope[`${param.name}Options`] = getIndexedFields(param);
          }

          if (fields) {
            const hasIndexedFields = fields.length > 0;
            const isExtraParam = i > 0;
            if (!hasIndexedFields && isExtraParam) { // don't draw the rest of the options if there are no indexed fields.
              return;
            }
          }


          let type = 'basic';
          if (param.advanced) type = 'advanced';

          if (aggParam = getAggParamHTML(param, i)) {
            aggParamHTML[type].push(aggParam);
          }

        });

        // compile the paramEditors html elements
        let paramEditors = aggParamHTML.basic;

        if (aggParamHTML.advanced.length) {
          paramEditors.push($(advancedToggleHtml).get(0));
          paramEditors = paramEditors.concat(aggParamHTML.advanced);
        }

        $aggParamEditors = $(paramEditors).appendTo($el);
        $compile($aggParamEditors)($aggParamEditorsScope);
      }

      // build HTML editor given an aggParam and index
      function getAggParamHTML(param, idx) {
        // don't show params without an editor
        if (!param.editor) {
          return;
        }

        const attrs = {
          'agg-param': 'agg.type.params[' + idx + ']'
        };

        if (param.advanced) {
          attrs['ng-show'] = 'advancedToggled';
        }

        return $('<vis-agg-param-editor>')
        .attr(attrs)
        .append(param.editor)
        .get(0);
      }

      function getIndexedFields(param) {
        let fields = _.filter($scope.agg.vis.indexPattern.fields.raw, 'aggregatable');
        const fieldTypes = param.filterFieldTypes;

        if (fieldTypes) {
          const filter = _.isFunction(fieldTypes) ? fieldTypes.bind(this, $scope.agg.vis) : fieldTypes;
          fields = $filter('fieldType')(fields, filter);
          fields = $filter('orderBy')(fields, ['type', 'name']);
        }

        return new IndexedArray({

          /**
           * @type {Array}
           */
          index: ['name'],

          /**
           * [group description]
           * @type {Array}
           */
          initialSet: fields
        });
      }
    }
  };
});
