text: import { saveAs } from '@elastic/filesaver';
  import { find, flattenDeep, pluck, sortBy } from 'lodash';
  import angular from 'angular';
  import { savedObjectManagementRegistry } from 'plugins/kibana/management/saved_object_registry';
  import objectIndexHTML from 'plugins/kibana/management/sections/objects/_objects.html';
  import 'ui/directives/file_upload';
  import uiRoutes from 'ui/routes';
  import { SavedObjectsClientProvider } from 'ui/saved_objects';
  import { uiModules } from 'ui/modules';
  import { showChangeIndexModal } from './show_change_index_modal';
  import { SavedObjectNotFound } from 'ui/errors';
  
  const indexPatternsResolutions = {
    indexPatterns: function (Private) {
      const savedObjectsClient = Private(SavedObjectsClientProvider);
  
      return savedObjectsClient.find({
        type: 'index-pattern',
        fields: ['title'],
        perPage: 10000
      }).then(response => response.savedObjects);
    }
  };
  
  uiRoutes
    .when('/management/kibana/objects', {
      template: objectIndexHTML,
      resolve: indexPatternsResolutions
    });
  
  uiRoutes
    .when('/management/kibana/objects/:service', {
      redirectTo: '/management/kibana/objects'
    });
  
  uiModules.get('apps/management')
    .directive('kbnManagementObjects', function ($route, kbnIndex, Notifier, Private, kbnUrl, Promise, confirmModal) {
      const savedObjectsClient = Private(SavedObjectsClientProvider);
  
      return {
        restrict: 'E',
        controllerAs: 'managementObjectsController',
        controller: function ($scope, $injector, $q, AppState) {
          const notify = new Notifier({ location: 'Saved Objects' });
  
          // TODO: Migrate all scope variables to the controller.
          const $state = $scope.state = new AppState();
          $scope.currentTab = null;
          $scope.selectedItems = [];
  
          this.areAllRowsChecked = function areAllRowsChecked() {
            if ($scope.currentTab.data.length === 0) {
              return false;
            }
            return $scope.selectedItems.length === $scope.currentTab.data.length;
          };
  
          const getData = function (filter) {
            const services = savedObjectManagementRegistry.all().map(function (obj) {
              const service = $injector.get(obj.service);
              return service.findAll(filter).then(function (data) {
                return {
                  service: service,
                  serviceName: obj.service,
                  title: obj.title,
                  type: service.type,
                  data: data.hits,
                  total: data.total
                };
              });
            });
  
            $q.all(services).then(function (data) {
              $scope.services = sortBy(data, 'title');
              if ($state.tab) $scope.currentTab = find($scope.services, { title: $state.tab });
  
              $scope.$watch('state.tab', function (tab) {
                if (!tab) $scope.changeTab($scope.services[0]);
              });
            });
          };
  
          const refreshData = () => {
            return getData(this.advancedFilter);
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.toggleAll = function () {
            if ($scope.selectedItems.length === $scope.currentTab.data.length) {
              $scope.selectedItems.length = 0;
            } else {
              $scope.selectedItems = [].concat($scope.currentTab.data);
            }
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.toggleItem = function (item) {
            const i = $scope.selectedItems.indexOf(item);
            if (i >= 0) {
              $scope.selectedItems.splice(i, 1);
            } else {
              $scope.selectedItems.push(item);
            }
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.open = function (item) {
            kbnUrl.change(item.url.substr(1));
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.edit = function (service, item) {
            const params = {
              service: service.serviceName,
              id: item.id
            };
  
            kbnUrl.change('/management/kibana/objects/{{ service }}/{{ id }}', params);
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.bulkDelete = function () {
            function doBulkDelete() {
              $scope.currentTab.service.delete(pluck($scope.selectedItems, 'id'))
                .then(refreshData)
                .then(function () {
                  $scope.selectedItems.length = 0;
                })
                .catch(error => notify.error(error));
            }
  
            const confirmModalOptions = {
              confirmButtonText: 'Delete',
              onConfirm: doBulkDelete,
              title: `Delete selected ${$scope.currentTab.title}?`
            };
            confirmModal(
              `You can't recover deleted ${$scope.currentTab.title}.`,
              confirmModalOptions
            );
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.bulkExport = function () {
            const objs = $scope.selectedItems.map(item => {
              return { type: $scope.currentTab.type, id: item.id };
            });
  
            retrieveAndExportDocs(objs);
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.exportAll = () => Promise
            .map($scope.services, service => service.service
              .scanAll('')
              .then(result => result.hits)
            )
            .then(results => saveToFile(flattenDeep(results)))
            .catch(error => notify.error(error));
  
          function retrieveAndExportDocs(objs) {
            if (!objs.length) return notify.error('No saved objects to export.');
  
            savedObjectsClient.bulkGet(objs)
              .then(function (response) {
                saveToFile(response.savedObjects.map(obj => {
                  return {
                    _id: obj.id,
                    _type: obj.type,
                    _source: obj.attributes
                  };
                }));
              });
          }
  
          function saveToFile(results) {
            const blob = new Blob([angular.toJson(results, true)], { type: 'application/json' });
            saveAs(blob, 'export.json');
          }
  
          // TODO: Migrate all scope methods to the controller.
          $scope.importAll = function (fileContents) {
            let docs;
            try {
              docs = JSON.parse(fileContents);
            } catch (e) {
              notify.error('The file could not be processed.');
              return;
            }
  
            // make sure we have an array, show an error otherwise
            if (!Array.isArray(docs)) {
              notify.error('Saved objects file format is invalid and cannot be imported.');
              return;
            }
  
            return new Promise((resolve) => {
              confirmModal(
                '', {
                  confirmButtonText: `Yes, overwrite all objects`,
                  cancelButtonText: `No, prompt for each object`,
                  onConfirm: () => resolve(true),
                  onCancel: () => resolve(false),
                  title: 'Automatically overwrite all saved objects?'
                }
              );
            })
              .then((overwriteAll) => {
                // Keep a record of the index patterns assigned to our imported saved objects that do not
                // exist. We will provide a way for the user to manually select a new index pattern for those
                // saved objects.
                const conflictedIndexPatterns = [];
                // We want to do the same for saved searches, but we want to keep them separate because they need
                // to be applied _first_ because other saved objects can be depedent on those saved searches existing
                const conflictedSearchDocs = [];
  
                function importDocument(swallowErrors, doc) {
                  const { service } = find($scope.services, { type: doc._type }) || {};
  
                  if (!service) {
                    const msg = `Skipped import of "${doc._source.title}" (${doc._id})`;
                    const reason = `Invalid type: "${doc._type}"`;
  
                    notify.warning(`${msg}, ${reason}`, {
                      lifetime: 0,
                    });
  
                    return;
                  }
  
                  return service.get()
                    .then(function (obj) {
                      obj.id = doc._id;
                      return obj.applyESResp(doc)
                        .then(() => {
                          return obj.save({ confirmOverwrite: !overwriteAll });
                        })
                        .catch((err) => {
                          if (swallowErrors && err instanceof SavedObjectNotFound) {
                            switch (err.savedObjectType) {
                              case 'search':
                                conflictedSearchDocs.push(doc);
                                return;
                              case 'index-pattern':
                                conflictedIndexPatterns.push({ obj, doc });
                                return;
                            }
                          }
                          // swallow errors here so that the remaining promise chain executes
                          err.message = `Importing ${obj.title} (${obj.id}) failed: ${err.message}`;
                          notify.error(err);
                        });
                    });
                }
  
                function groupByType(docs) {
                  const defaultDocTypes = {
                    searches: [],
                    other: [],
                  };
  
                  return docs.reduce((types, doc) => {
                    switch (doc._type) {
                      case 'search':
                        types.searches.push(doc);
                        break;
                      default:
                        types.other.push(doc);
                    }
                    return types;
                  }, defaultDocTypes);
                }
  
                function resolveConflicts(objs, { obj }) {
                  const oldIndexId = obj.searchSource.getOwn('index');
                  const newIndexId = objs.find(({ oldId }) => oldId === oldIndexId).newId;
                  // If the user did not select a new index pattern in the modal, the id
                  // will be same as before, so don't try to update it
                  if (newIndexId === oldIndexId) {
                    return;
                  }
                  return obj.hydrateIndexPattern(newIndexId)
                    .then(() => obj.save({ confirmOverwrite: !overwriteAll }));
                }
  
                const docTypes = groupByType(docs);
  
                return Promise.map(docTypes.searches, importDocument.bind(null, true))
                  .then(() => Promise.map(docTypes.other, importDocument.bind(null, true)))
                  .then(() => {
                    if (conflictedIndexPatterns.length) {
                      return new Promise((resolve, reject) => {
                        showChangeIndexModal(
                          (objs) => {
                            Promise.map(conflictedIndexPatterns, resolveConflicts.bind(null, objs))
                              .then(resolve)
                              .catch(reject);
                          },
                          conflictedIndexPatterns,
                          $route.current.locals.indexPatterns,
                        );
                      });
                    }
                  })
                  .then(() => Promise.map(conflictedSearchDocs, importDocument.bind(null, false)))
                  .then(refreshData)
                  .catch(notify.error);
              });
          };
  
          // TODO: Migrate all scope methods to the controller.
          $scope.changeTab = function (tab) {
            $scope.currentTab = tab;
            $scope.selectedItems.length = 0;
            $state.tab = tab.title;
            $state.save();
          };
  
          $scope.$watch('managementObjectsController.advancedFilter', function (filter) {
            getData(filter);
          });
        }
      };
    });
  