"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getDiscoverAppStateContainer = exports.DiscoverAppStateProvider = exports.APP_STATE_URL_KEY = void 0;
exports.getInitialState = getInitialState;
exports.isEqualFilters = isEqualFilters;
exports.isEqualState = isEqualState;
exports.setState = setState;
exports.useAppStateSelector = void 0;
var _common = require("@kbn/kibana-utils-plugin/common");
var _esQuery = require("@kbn/es-query");
var _public = require("@kbn/kibana-utils-plugin/public");
var _lodash = require("lodash");
var _public2 = require("@kbn/data-plugin/public");
var _add_log = require("../../../utils/add_log");
var _cleanup_url_state = require("../utils/cleanup_url_state");
var _get_state_defaults = require("../utils/get_state_defaults");
var _state_helpers = require("../../../utils/state_helpers");
/*
 * 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 APP_STATE_URL_KEY = '_a';
exports.APP_STATE_URL_KEY = APP_STATE_URL_KEY;
const {
  Provider: DiscoverAppStateProvider,
  useSelector: useAppStateSelector
} = (0, _common.createStateContainerReactHelpers)();

/**
 * This is the app state container for Discover main, it's responsible for syncing state with the URL
 * @param stateStorage
 * @param savedSearch
 * @param services
 */
exports.useAppStateSelector = useAppStateSelector;
exports.DiscoverAppStateProvider = DiscoverAppStateProvider;
const getDiscoverAppStateContainer = ({
  stateStorage,
  savedSearch,
  services
}) => {
  let initialState = getInitialState(stateStorage, savedSearch, services);
  let previousState = initialState;
  const appStateContainer = (0, _common.createStateContainer)(initialState);
  const enhancedAppContainer = {
    ...appStateContainer,
    set: value => {
      if (value) {
        previousState = appStateContainer.getState();
        appStateContainer.set(value);
      }
    }
  };
  const hasChanged = () => {
    return !isEqualState(initialState, appStateContainer.getState());
  };
  const getAppStateFromSavedSearch = newSavedSearch => {
    return (0, _get_state_defaults.getStateDefaults)({
      savedSearch: newSavedSearch,
      services
    });
  };
  const resetToState = state => {
    (0, _add_log.addLog)('[appState] reset state to', state);
    previousState = state;
    appStateContainer.set(state);
  };
  const resetInitialState = () => {
    (0, _add_log.addLog)('[appState] reset initial state to the current state');
    initialState = appStateContainer.getState();
  };
  const replaceUrlState = async (newPartial = {}, merge = true) => {
    (0, _add_log.addLog)('[appState] replaceUrlState', {
      newPartial,
      merge
    });
    const state = merge ? {
      ...appStateContainer.getState(),
      ...newPartial
    } : newPartial;
    await stateStorage.set(APP_STATE_URL_KEY, state, {
      replace: true
    });
  };
  const startAppStateUrlSync = () => {
    (0, _add_log.addLog)('[appState] start syncing state with URL');
    return (0, _public.syncState)({
      storageKey: APP_STATE_URL_KEY,
      stateContainer: enhancedAppContainer,
      stateStorage
    });
  };
  const initializeAndSync = currentSavedSearch => {
    (0, _add_log.addLog)('[appState] initialize state and sync with URL', currentSavedSearch);
    const {
      data
    } = services;
    const dataView = currentSavedSearch.searchSource.getField('index');
    if (appStateContainer.getState().index !== (dataView === null || dataView === void 0 ? void 0 : dataView.id)) {
      // used data view is different from the given by url/state which is invalid
      setState(appStateContainer, {
        index: dataView === null || dataView === void 0 ? void 0 : dataView.id
      });
    }
    // syncs `_a` portion of url with query services
    const stopSyncingQueryAppStateWithStateContainer = (0, _public2.connectToQueryState)(data.query, appStateContainer, {
      filters: _esQuery.FilterStateStore.APP_STATE,
      query: true
    });

    // syncs `_g` portion of url with query services
    const {
      stop: stopSyncingGlobalStateWithUrl
    } = (0, _public2.syncGlobalQueryStateWithUrl)(data.query, stateStorage);
    const {
      start,
      stop
    } = startAppStateUrlSync();
    // current state need to be pushed to url
    replaceUrlState({}).then(() => start());
    return () => {
      stopSyncingQueryAppStateWithStateContainer();
      stopSyncingGlobalStateWithUrl();
      stop();
    };
  };
  const update = (newPartial, replace = false) => {
    (0, _add_log.addLog)('[appState] update', {
      newPartial,
      replace
    });
    if (replace) {
      return replaceUrlState(newPartial);
    } else {
      previousState = {
        ...appStateContainer.getState()
      };
      setState(appStateContainer, newPartial);
    }
  };
  const isEmptyURL = () => {
    const urlValue = stateStorage.get(APP_STATE_URL_KEY);
    return urlValue === undefined || urlValue === null;
  };
  const getPrevious = () => previousState;
  return {
    ...enhancedAppContainer,
    isEmptyURL,
    getPrevious,
    hasChanged,
    initAndSync: initializeAndSync,
    resetToState,
    resetInitialState,
    replaceUrlState,
    syncState: startAppStateUrlSync,
    update,
    getAppStateFromSavedSearch
  };
};
exports.getDiscoverAppStateContainer = getDiscoverAppStateContainer;
function getInitialState(stateStorage, savedSearch, services) {
  const stateStorageURL = stateStorage === null || stateStorage === void 0 ? void 0 : stateStorage.get(APP_STATE_URL_KEY);
  const defaultAppState = (0, _get_state_defaults.getStateDefaults)({
    savedSearch,
    services
  });
  return (0, _state_helpers.handleSourceColumnState)(stateStorageURL === null ? defaultAppState : {
    ...defaultAppState,
    ...(0, _cleanup_url_state.cleanupUrlState)(stateStorageURL)
  }, services.uiSettings);
}

/**
 * Helper function to merge a given new state with the existing state and to set the given state
 * container
 */
function setState(stateContainer, newState) {
  (0, _add_log.addLog)('[appstate] setState', {
    newState
  });
  const oldState = stateContainer.getState();
  const mergedState = {
    ...oldState,
    ...newState
  };
  if (!isEqualState(oldState, mergedState)) {
    stateContainer.set(mergedState);
  }
}

/**
 * Helper function to compare 2 different filter states
 */
function isEqualFilters(filtersA, filtersB) {
  if (!filtersA && !filtersB) {
    return true;
  } else if (!filtersA || !filtersB) {
    return false;
  }
  return (0, _esQuery.compareFilters)(filtersA, filtersB, _esQuery.COMPARE_ALL_OPTIONS);
}

/**
 * Helper function to compare 2 different state, is needed since comparing filters
 * works differently
 */
function isEqualState(stateA, stateB) {
  if (!stateA && !stateB) {
    return true;
  } else if (!stateA || !stateB) {
    return false;
  }
  const {
    filters: stateAFilters = [],
    ...stateAPartial
  } = stateA;
  const {
    filters: stateBFilters = [],
    ...stateBPartial
  } = stateB;
  return (0, _lodash.isEqual)(stateAPartial, stateBPartial) && isEqualFilters(stateAFilters, stateBFilters);
}