"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fetchAll = fetchAll;
exports.fetchMoreDocuments = fetchMoreDocuments;
var _rxjs = require("rxjs");
var _ebtTools = require("@kbn/ebt-tools");
var _lodash = require("lodash");
var _update_search_source = require("./update_search_source");
var _get_raw_record_type = require("./get_raw_record_type");
var _use_saved_search_messages = require("../hooks/use_saved_search_messages");
var _fetch_documents = require("./fetch_documents");
var _types = require("../../types");
var _discover_data_state_container = require("../services/discover_data_state_container");
var _fetch_text_based = require("./fetch_text_based");
/*
 * 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.
 */

/**
 * This function starts fetching all required queries in Discover. This will be the query to load the individual
 * documents as well as any other requests that might be required to load the main view.
 *
 * This method returns a promise, which will resolve (without a value), as soon as all queries that have been started
 * have been completed (failed or successfully).
 */
function fetchAll(dataSubjects, reset = false, fetchDeps) {
  const {
    initialFetchStatus,
    getAppState,
    getInternalState,
    services,
    inspectorAdapters,
    savedSearch,
    abortController
  } = fetchDeps;
  const {
    data
  } = services;
  const searchSource = savedSearch.searchSource.createChild();
  try {
    const dataView = searchSource.getField('index');
    const query = getAppState().query;
    const prevQuery = dataSubjects.documents$.getValue().query;
    const recordRawType = (0, _get_raw_record_type.getRawRecordType)(query);
    const useTextbased = recordRawType === _discover_data_state_container.RecordRawType.PLAIN;
    if (reset) {
      (0, _use_saved_search_messages.sendResetMsg)(dataSubjects, initialFetchStatus, recordRawType);
    }
    if (recordRawType === _discover_data_state_container.RecordRawType.DOCUMENT) {
      // Update the base searchSource, base for all child fetches
      (0, _update_search_source.updateVolatileSearchSource)(searchSource, {
        dataView,
        services,
        sort: getAppState().sort,
        customFilters: getInternalState().customFilters
      });
    }

    // Mark all subjects as loading
    (0, _use_saved_search_messages.sendLoadingMsg)(dataSubjects.main$, {
      recordRawType
    });
    (0, _use_saved_search_messages.sendLoadingMsg)(dataSubjects.documents$, {
      recordRawType,
      query
    });
    (0, _use_saved_search_messages.sendLoadingMsg)(dataSubjects.totalHits$, {
      recordRawType
    });

    // Start fetching all required requests
    const response = useTextbased && query ? (0, _fetch_text_based.fetchTextBased)(query, dataView, data, services.expressions, inspectorAdapters, abortController.signal) : (0, _fetch_documents.fetchDocuments)(searchSource, fetchDeps);
    const fetchType = useTextbased && query ? 'fetchTextBased' : 'fetchDocuments';
    const startTime = window.performance.now();
    // Handle results of the individual queries and forward the results to the corresponding dataSubjects
    response.then(({
      records,
      textBasedQueryColumns,
      interceptedWarnings,
      textBasedHeaderWarning
    }) => {
      if (services.analytics) {
        const duration = window.performance.now() - startTime;
        (0, _ebtTools.reportPerformanceMetricEvent)(services.analytics, {
          eventName: 'discoverFetchAllRequestsOnly',
          duration,
          meta: {
            fetchType
          }
        });
      }
      // If the total hits (or chart) query is still loading, emit a partial
      // hit count that's at least our retrieved document count
      if (dataSubjects.totalHits$.getValue().fetchStatus === _types.FetchStatus.LOADING) {
        dataSubjects.totalHits$.next({
          fetchStatus: _types.FetchStatus.PARTIAL,
          result: records.length,
          recordRawType
        });
      }
      /**
       * The partial state for text based query languages is necessary in case the query has changed
       * In the follow up useTextBasedQueryLanguage hook in this case new columns are added to AppState
       * So the data table shows the new columns of the table. The partial state was introduced to prevent
       * To frequent change of state causing the table to re-render to often, which causes race conditions
       * So it takes too long, a bad user experience, also a potential flakniess in tests
       */
      const fetchStatus = useTextbased && (!prevQuery || !(0, _lodash.isEqual)(query, prevQuery)) ? _types.FetchStatus.PARTIAL : _types.FetchStatus.COMPLETE;
      dataSubjects.documents$.next({
        fetchStatus,
        result: records,
        textBasedQueryColumns,
        textBasedHeaderWarning,
        interceptedWarnings,
        recordRawType,
        query
      });
      (0, _use_saved_search_messages.checkHitCount)(dataSubjects.main$, records.length);
    })
    // In the case that the request was aborted (e.g. a refresh), swallow the abort error
    .catch(e => {
      if (!abortController.signal.aborted) throw e;
    })
    // Only the document query should send its errors to main$, to cause the full Discover app
    // to get into an error state. The other queries will not cause all of Discover to error out
    // but their errors will be shown in-place (e.g. of the chart).
    .catch((0, _use_saved_search_messages.sendErrorTo)(dataSubjects.documents$, dataSubjects.main$));

    // Return a promise that will resolve once all the requests have finished or failed
    return (0, _rxjs.firstValueFrom)((0, _rxjs.merge)(fetchStatusByType(dataSubjects.documents$, 'documents'), fetchStatusByType(dataSubjects.totalHits$, 'totalHits')).pipe((0, _rxjs.scan)(toRequestFinishedMap, {}), (0, _rxjs.filter)(allRequestsFinished))).then(() => {
      // Send a complete message to main$ once all queries are done and if main$
      // is not already in an ERROR state, e.g. because the document query has failed.
      // This will only complete main$, if it hasn't already been completed previously
      // by a query finding no results.
      if (dataSubjects.main$.getValue().fetchStatus !== _types.FetchStatus.ERROR) {
        (0, _use_saved_search_messages.sendCompleteMsg)(dataSubjects.main$);
      }
    });
  } catch (error) {
    (0, _use_saved_search_messages.sendErrorMsg)(dataSubjects.main$, error);
    // We also want to return a resolved promise in an error case, since it just indicates we're done with querying.
    return Promise.resolve();
  }
}
async function fetchMoreDocuments(dataSubjects, fetchDeps) {
  try {
    var _lastDocuments, _lastDocuments$raw;
    const {
      getAppState,
      getInternalState,
      services,
      savedSearch
    } = fetchDeps;
    const searchSource = savedSearch.searchSource.createChild();
    const dataView = searchSource.getField('index');
    const query = getAppState().query;
    const recordRawType = (0, _get_raw_record_type.getRawRecordType)(query);
    if (recordRawType === _discover_data_state_container.RecordRawType.PLAIN) {
      // not supported yet
      return;
    }
    const lastDocuments = dataSubjects.documents$.getValue().result || [];
    const lastDocumentSort = (_lastDocuments = lastDocuments[lastDocuments.length - 1]) === null || _lastDocuments === void 0 ? void 0 : (_lastDocuments$raw = _lastDocuments.raw) === null || _lastDocuments$raw === void 0 ? void 0 : _lastDocuments$raw.sort;
    if (!lastDocumentSort) {
      return;
    }
    searchSource.setField('searchAfter', lastDocumentSort);

    // Mark as loading
    (0, _use_saved_search_messages.sendLoadingMoreMsg)(dataSubjects.documents$);

    // Update the searchSource
    (0, _update_search_source.updateVolatileSearchSource)(searchSource, {
      dataView,
      services,
      sort: getAppState().sort,
      customFilters: getInternalState().customFilters
    });

    // Fetch more documents
    const {
      records,
      interceptedWarnings
    } = await (0, _fetch_documents.fetchDocuments)(searchSource, fetchDeps);

    // Update the state and finish the loading state
    (0, _use_saved_search_messages.sendLoadingMoreFinishedMsg)(dataSubjects.documents$, {
      moreRecords: records,
      interceptedWarnings
    });
  } catch (error) {
    (0, _use_saved_search_messages.sendLoadingMoreFinishedMsg)(dataSubjects.documents$, {
      moreRecords: [],
      interceptedWarnings: undefined
    });
    (0, _use_saved_search_messages.sendErrorTo)(dataSubjects.main$)(error);
  }
}
const fetchStatusByType = (subject, type) => subject.pipe((0, _rxjs.map)(({
  fetchStatus
}) => ({
  type,
  fetchStatus
})));
const toRequestFinishedMap = (currentMap, {
  type,
  fetchStatus
}) => ({
  ...currentMap,
  [type]: [_types.FetchStatus.COMPLETE, _types.FetchStatus.ERROR].includes(fetchStatus)
});
const allRequestsFinished = requests => Object.values(requests).every(finished => finished);