"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.stateSelectorFactory = exports.matchedIndiciesDefault = exports.DataViewEditorService = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _rxjs = require("rxjs");
var _public = require("@kbn/data-views-plugin/public");
var _lib = require("./lib");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

const matchedIndiciesDefault = {
  allIndices: [],
  exactMatchedIndices: [],
  partialMatchedIndices: [],
  visibleIndices: []
};

/**
 * ConstructorArgs for DataViewEditorService
 */
exports.matchedIndiciesDefault = matchedIndiciesDefault;
const defaultDataViewEditorState = {
  matchedIndices: {
    ...matchedIndiciesDefault
  },
  rollupIndicesCaps: {},
  isLoadingSourcesInternal: false,
  loadingTimestampFields: false,
  timestampFieldOptions: [],
  rollupIndexName: undefined
};
const stateSelectorFactory = state$ => (selector, equalityFn) => state$.pipe((0, _rxjs.map)(selector), (0, _rxjs.distinctUntilChanged)(equalityFn));
exports.stateSelectorFactory = stateSelectorFactory;
class DataViewEditorService {
  constructor({
    services: {
      http,
      dataViews
    },
    initialValues: {
      type: initialType = _public.INDEX_PATTERN_TYPE.DEFAULT,
      indexPattern: initialIndexPattern = '',
      name: _initialName = ''
    },
    requireTimestampField: _requireTimestampField = false
  }) {
    (0, _defineProperty2.default)(this, "http", void 0);
    (0, _defineProperty2.default)(this, "dataViews", void 0);
    // config
    (0, _defineProperty2.default)(this, "requireTimestampField", void 0);
    (0, _defineProperty2.default)(this, "type", _public.INDEX_PATTERN_TYPE.DEFAULT);
    (0, _defineProperty2.default)(this, "indexPattern", '');
    (0, _defineProperty2.default)(this, "allowHidden", false);
    // used for data view name validation - no dupes!
    (0, _defineProperty2.default)(this, "dataViewNames$", void 0);
    (0, _defineProperty2.default)(this, "loadTimestampFieldsSub", void 0);
    (0, _defineProperty2.default)(this, "matchedIndicesForProviderSub", void 0);
    (0, _defineProperty2.default)(this, "rollupIndexForProviderSub", void 0);
    (0, _defineProperty2.default)(this, "state$", void 0);
    // used for validating rollup data views - must match one and only one data view
    (0, _defineProperty2.default)(this, "rollupIndicesCaps$", void 0);
    (0, _defineProperty2.default)(this, "isLoadingSources$", void 0);
    (0, _defineProperty2.default)(this, "loadingTimestampFields$", void 0);
    (0, _defineProperty2.default)(this, "timestampFieldOptions$", void 0);
    // current matched rollup index
    (0, _defineProperty2.default)(this, "rollupIndex$", void 0);
    // alernates between value and undefined so validation can treat new value as thought its a promise
    (0, _defineProperty2.default)(this, "rollupIndexForProvider$", new _rxjs.Subject());
    (0, _defineProperty2.default)(this, "matchedIndices$", void 0);
    // alernates between value and undefined so validation can treat new value as thought its a promise
    (0, _defineProperty2.default)(this, "matchedIndicesForProvider$", new _rxjs.Subject());
    (0, _defineProperty2.default)(this, "rollupCapsResponse", void 0);
    (0, _defineProperty2.default)(this, "currentLoadingTimestampFields", 0);
    (0, _defineProperty2.default)(this, "currentLoadingMatchedIndices", 0);
    (0, _defineProperty2.default)(this, "updateState", newState => {
      this.state$.next({
        ...this.state$.getValue(),
        ...newState
      });
    });
    (0, _defineProperty2.default)(this, "getRollupIndexCaps", async () => {
      let rollupIndicesCaps = {};
      try {
        rollupIndicesCaps = await this.http.get('/api/rollup/indices');
      } catch (e) {
        // Silently swallow failure responses such as expired trials
      }
      this.updateState({
        rollupIndicesCaps
      });
      return rollupIndicesCaps;
    });
    (0, _defineProperty2.default)(this, "getIsRollupIndex", async () => {
      const response = await this.rollupCapsResponse;
      const indices = Object.keys(response);
      return indexName => indices.includes(indexName);
    });
    (0, _defineProperty2.default)(this, "loadMatchedIndices", async (query, allowHidden, allSources, type) => {
      const currentLoadingMatchedIndicesIdx = ++this.currentLoadingMatchedIndices;
      const isRollupIndex = await this.getIsRollupIndex();
      const indexRequests = [];
      let newRollupIndexName;
      this.updateState({
        loadingTimestampFields: true
      });
      if (query !== null && query !== void 0 && query.endsWith('*')) {
        const exactMatchedQuery = this.getIndicesCached({
          pattern: query,
          showAllIndices: allowHidden
        });
        indexRequests.push(exactMatchedQuery);
        // provide default value when not making a request for the partialMatchQuery
        indexRequests.push(Promise.resolve([]));
      } else {
        const exactMatchQuery = this.getIndicesCached({
          pattern: query,
          showAllIndices: allowHidden
        });
        const partialMatchQuery = this.getIndicesCached({
          pattern: `${query}*`,
          showAllIndices: allowHidden
        });
        indexRequests.push(exactMatchQuery);
        indexRequests.push(partialMatchQuery);
      }
      const [exactMatched, partialMatched] = await (0, _lib.ensureMinimumTime)(indexRequests);
      const matchedIndices = (0, _lib.getMatchedIndices)(allSources, partialMatched, exactMatched, allowHidden);

      // verify we're looking at the current result
      if (currentLoadingMatchedIndicesIdx === this.currentLoadingMatchedIndices) {
        if (type === _public.INDEX_PATTERN_TYPE.ROLLUP) {
          const rollupIndices = exactMatched.filter(index => isRollupIndex(index.name));
          newRollupIndexName = rollupIndices.length === 1 ? rollupIndices[0].name : null;
          this.updateState({
            rollupIndexName: newRollupIndexName
          });
        } else {
          this.updateState({
            rollupIndexName: null
          });
        }
        this.updateState({
          matchedIndices
        });
      }
    });
    (0, _defineProperty2.default)(this, "setIndexPattern", indexPattern => {
      this.indexPattern = (0, _lib.removeSpaces)(indexPattern);
      this.loadIndices();
    });
    (0, _defineProperty2.default)(this, "setAllowHidden", allowHidden => {
      this.allowHidden = allowHidden;
      this.loadIndices();
    });
    (0, _defineProperty2.default)(this, "setType", type => {
      this.type = type;
      this.loadIndices();
    });
    (0, _defineProperty2.default)(this, "loadIndices", async () => {
      const allSrcs = await this.getIndicesCached({
        pattern: '*',
        showAllIndices: this.allowHidden
      });
      await this.loadMatchedIndices(this.indexPattern, this.allowHidden, allSrcs, this.type);
      this.updateState({
        isLoadingSourcesInternal: false
      });
    });
    (0, _defineProperty2.default)(this, "loadDataViewNames", async initialName => {
      const dataViewListItems = await this.dataViews.getIdsWithTitle(true);
      const dataViewNames = dataViewListItems.map(item => item.name || item.title);
      return initialName ? dataViewNames.filter(v => v !== initialName) : dataViewNames;
    });
    (0, _defineProperty2.default)(this, "getIndicesMemory", {});
    (0, _defineProperty2.default)(this, "getIndicesCached", async props => {
      const key = JSON.stringify(props);
      this.getIndicesMemory[key] = this.getIndicesMemory[key] || this.getIsRollupIndex().then(isRollupIndex => this.dataViews.getIndices({
        ...props,
        isRollupIndex
      }));
      this.getIndicesMemory[key].catch(() => {
        delete this.getIndicesMemory[key];
      });
      return await this.getIndicesMemory[key];
    });
    (0, _defineProperty2.default)(this, "timestampOptionsMemory", {});
    (0, _defineProperty2.default)(this, "getTimestampOptionsForWildcard", async (getFieldsOptions, requireTimestampField) => {
      const fields = await (0, _lib.ensureMinimumTime)(this.dataViews.getFieldsForWildcard(getFieldsOptions));
      return (0, _lib.extractTimeFields)(fields, requireTimestampField);
    });
    (0, _defineProperty2.default)(this, "getTimestampOptionsForWildcardCached", async (getFieldsOptions, requireTimestampField) => {
      const key = JSON.stringify(getFieldsOptions) + requireTimestampField;
      const getTimestampOptionsPromise = this.getTimestampOptionsForWildcard(getFieldsOptions, requireTimestampField);
      this.timestampOptionsMemory[key] = this.timestampOptionsMemory[key] || getTimestampOptionsPromise;
      getTimestampOptionsPromise.catch(() => {
        delete this.timestampOptionsMemory[key];
      });
      return await getTimestampOptionsPromise;
    });
    (0, _defineProperty2.default)(this, "loadTimestampFields", async () => {
      const currentState = this.state$.getValue();
      if (currentState.matchedIndices.exactMatchedIndices.length === 0) {
        this.updateState({
          timestampFieldOptions: [],
          loadingTimestampFields: false
        });
        return;
      }
      const currentLoadingTimestampFieldsIdx = ++this.currentLoadingTimestampFields;
      const getFieldsOptions = {
        pattern: this.indexPattern
      };
      if (this.type === _public.INDEX_PATTERN_TYPE.ROLLUP) {
        getFieldsOptions.type = _public.INDEX_PATTERN_TYPE.ROLLUP;
        getFieldsOptions.rollupIndex = currentState.rollupIndexName || '';
        getFieldsOptions.allowNoIndex = true;
      }
      let timestampFieldOptions = [];
      try {
        timestampFieldOptions = await this.getTimestampOptionsForWildcardCached(getFieldsOptions, this.requireTimestampField);
      } finally {
        if (currentLoadingTimestampFieldsIdx === this.currentLoadingTimestampFields) {
          this.updateState({
            timestampFieldOptions,
            loadingTimestampFields: false
          });
        }
      }
    });
    // provides info necessary for validation of index pattern in required async format
    (0, _defineProperty2.default)(this, "indexPatternValidationProvider", async () => {
      const rollupIndexPromise = (0, _rxjs.firstValueFrom)(this.rollupIndex$.pipe((0, _rxjs.first)(data => data !== undefined)));
      const matchedIndicesPromise = (0, _rxjs.firstValueFrom)(this.matchedIndicesForProvider$.pipe((0, _rxjs.first)(data => data !== undefined)));

      // necessary to get new observable value if the field hasn't changed
      this.loadIndices();

      // Wait until we have fetched the indices.
      // The result will then be sent to the field validator(s) (when calling await provider(););
      const [rollupIndex, matchedIndices] = await Promise.all([rollupIndexPromise, matchedIndicesPromise]);
      return {
        rollupIndex,
        matchedIndices: matchedIndices || matchedIndiciesDefault
      };
    });
    (0, _defineProperty2.default)(this, "destroy", () => {
      this.loadTimestampFieldsSub.unsubscribe();
      this.matchedIndicesForProviderSub.unsubscribe();
      this.rollupIndexForProviderSub.unsubscribe();
    });
    this.http = http;
    this.dataViews = dataViews;
    this.requireTimestampField = _requireTimestampField;
    this.type = initialType;
    this.indexPattern = (0, _lib.removeSpaces)(initialIndexPattern);

    // fire off a couple of requests that we know we'll need
    this.rollupCapsResponse = this.getRollupIndexCaps();
    this.dataViewNames$ = (0, _rxjs.from)(this.loadDataViewNames(_initialName));
    this.state$ = new _rxjs.BehaviorSubject({
      ...defaultDataViewEditorState
    });
    const stateSelector = stateSelectorFactory(this.state$);

    // public observables
    this.matchedIndices$ = stateSelector(state => state.matchedIndices);
    this.rollupIndicesCaps$ = stateSelector(state => state.rollupIndicesCaps);
    this.isLoadingSources$ = stateSelector(state => state.isLoadingSourcesInternal);
    this.loadingTimestampFields$ = stateSelector(state => state.loadingTimestampFields);
    this.timestampFieldOptions$ = stateSelector(state => state.timestampFieldOptions);
    this.rollupIndex$ = stateSelector(state => state.rollupIndexName);

    // when list of matched indices is updated always update timestamp fields
    this.loadTimestampFieldsSub = this.matchedIndices$.subscribe(() => this.loadTimestampFields());

    // alternate value with undefined so validation knows when its getting a fresh value
    this.matchedIndicesForProviderSub = this.matchedIndices$.subscribe(matchedIndices => {
      this.matchedIndicesForProvider$.next(matchedIndices);
      this.matchedIndicesForProvider$.next(undefined);
    });

    // alternate value with undefined so validation knows when its getting a fresh value
    this.rollupIndexForProviderSub = this.rollupIndex$.subscribe(rollupIndex => {
      this.rollupIndexForProvider$.next(rollupIndex);
      this.rollupIndexForProvider$.next(undefined);
    });
  }
}
exports.DataViewEditorService = DataViewEditorService;