"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.RelevanceTuningLogic = void 0;
var _kea = require("kea");
var _lodash = require("lodash");
var _flash_messages = require("../../../shared/flash_messages");
var _http = require("../../../shared/http");
var _engine = require("../engine");
var _constants = require("./constants");
var _utils = require("./utils");
/*
 * 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; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

const RelevanceTuningLogic = (0, _kea.kea)({
  path: ['enterprise_search', 'app_search', 'relevance_tuning_logic'],
  actions: () => ({
    onInitializeRelevanceTuning: props => props,
    setSearchSettings: searchSettings => ({
      searchSettings
    }),
    setFilterValue: value => value,
    setSearchQuery: query => query,
    setSearchResults: searchResults => searchResults,
    setResultsLoading: resultsLoading => resultsLoading,
    clearSearchResults: true,
    resetSearchSettingsState: true,
    initializeRelevanceTuning: true,
    getSearchResults: true,
    setSearchSettingsResponse: searchSettings => ({
      searchSettings
    }),
    onSearchSettingsSuccess: searchSettings => ({
      searchSettings
    }),
    onSearchSettingsError: () => true,
    updateSearchSettings: true,
    resetSearchSettings: true,
    toggleSearchField: (name, disableField) => ({
      name,
      disableField
    }),
    updateFieldWeight: (name, weight) => ({
      name,
      weight
    }),
    addBoost: (name, type) => ({
      name,
      type
    }),
    deleteBoost: (name, index) => ({
      name,
      index
    }),
    updateBoostFactor: (name, index, factor) => ({
      name,
      index,
      factor
    }),
    updateBoostValue: (name, boostIndex, updatedValues) => ({
      name,
      boostIndex,
      updatedValues
    }),
    updateBoostCenter: (name, boostIndex, value) => ({
      name,
      boostIndex,
      value
    }),
    updateBoostSelectOption: (name, boostIndex, optionType, value) => ({
      name,
      boostIndex,
      optionType,
      value
    }),
    setPrecision: precision => ({
      precision
    })
  }),
  reducers: () => ({
    searchSettings: [{
      search_fields: {},
      boosts: {},
      precision: 2,
      precision_enabled: false
    }, {
      onInitializeRelevanceTuning: (_, {
        searchSettings
      }) => searchSettings,
      setSearchSettings: (_, {
        searchSettings
      }) => searchSettings,
      setSearchSettingsResponse: (_, {
        searchSettings
      }) => searchSettings,
      setPrecision: (currentSearchSettings, {
        precision
      }) => ({
        ...currentSearchSettings,
        precision
      })
    }],
    schema: [{}, {
      onInitializeRelevanceTuning: (_, {
        schema
      }) => schema
    }],
    schemaConflicts: [{}, {
      onInitializeRelevanceTuning: (_, {
        schemaConflicts
      }) => schemaConflicts || {}
    }],
    filterInputValue: ['', {
      setFilterValue: (_, filterInputValue) => filterInputValue
    }],
    query: ['', {
      setSearchQuery: (_, query) => query
    }],
    unsavedChanges: [false, {
      setPrecision: () => true,
      setSearchSettings: () => true,
      setSearchSettingsResponse: () => false
    }],
    dataLoading: [true, {
      onInitializeRelevanceTuning: () => false,
      resetSearchSettingsState: () => true
    }],
    searchResults: [null, {
      clearSearchResults: () => null,
      setSearchResults: (_, searchResults) => searchResults
    }],
    resultsLoading: [false, {
      setResultsLoading: (_, resultsLoading) => resultsLoading,
      setSearchResults: () => false
    }]
  }),
  selectors: ({
    selectors
  }) => ({
    schemaFields: [() => [selectors.schema], schema => Object.entries(schema).reduce((fields, [fieldName, {
      capabilities: fieldCapabilities
    }]) => {
      return fieldCapabilities.fulltext || fieldCapabilities.boost ? [...fields, fieldName] : fields;
    }, [])],
    schemaFieldsWithConflicts: [() => [selectors.schemaConflicts], schemaConflicts => Object.keys(schemaConflicts)],
    filteredSchemaFields: [() => [selectors.schemaFields, selectors.filterInputValue], (schemaFields, filterInputValue) => (0, _utils.filterIfTerm)(schemaFields, filterInputValue)],
    filteredSchemaFieldsWithConflicts: [() => [selectors.schemaFieldsWithConflicts, selectors.filterInputValue], (schemaFieldsWithConflicts, filterInputValue) => (0, _utils.filterIfTerm)(schemaFieldsWithConflicts, filterInputValue)],
    engineHasSchemaFields: [() => [selectors.schemaFields], schemaFields => schemaFields.length > 0],
    isPrecisionTuningEnabled: [() => [selectors.searchSettings], searchSettings => searchSettings.precision_enabled]
  }),
  listeners: ({
    actions,
    values
  }) => ({
    initializeRelevanceTuning: async () => {
      const {
        http
      } = _http.HttpLogic.values;
      const {
        engineName
      } = _engine.EngineLogic.values;
      const url = `/internal/app_search/engines/${engineName}/search_settings/details`;
      try {
        const response = await http.get(url);
        actions.onInitializeRelevanceTuning({
          ...response,
          searchSettings: {
            ...response.searchSettings,
            boosts: (0, _utils.normalizeBoostValues)(response.searchSettings.boosts)
          }
        });
      } catch (e) {
        (0, _flash_messages.flashAPIErrors)(e);
      }
    },
    getSearchResults: async (_, breakpoint) => {
      await breakpoint(250);
      const query = values.query;
      if (!query) return actions.clearSearchResults();
      const {
        engineName
      } = _engine.EngineLogic.values;
      const {
        http
      } = _http.HttpLogic.values;
      const {
        search_fields: searchFields,
        boosts,
        precision
      } = (0, _utils.removeBoostStateProps)(values.searchSettings);
      const url = `/internal/app_search/engines/${engineName}/search`;
      actions.setResultsLoading(true);
      const filteredBoosts = (0, _utils.removeEmptyValueBoosts)(boosts);
      const precisionSettings = values.isPrecisionTuningEnabled ? {
        precision
      } : {};
      try {
        const response = await http.post(url, {
          query: {
            query
          },
          body: JSON.stringify({
            boosts: (0, _lodash.isEmpty)(filteredBoosts) ? undefined : filteredBoosts,
            search_fields: (0, _lodash.isEmpty)(searchFields) ? undefined : searchFields,
            ...precisionSettings
          })
        });
        actions.setSearchResults(response.results);
        (0, _flash_messages.clearFlashMessages)();
      } catch (e) {
        (0, _flash_messages.flashAPIErrors)(e);
      }
    },
    setSearchSettings: () => {
      actions.getSearchResults();
    },
    onSearchSettingsSuccess: ({
      searchSettings
    }) => {
      actions.setSearchSettingsResponse(searchSettings);
      actions.getSearchResults();
      window.scrollTo(0, 0);
    },
    onSearchSettingsError: () => {
      window.scrollTo(0, 0);
    },
    updateSearchSettings: async () => {
      const {
        http
      } = _http.HttpLogic.values;
      const {
        engineName
      } = _engine.EngineLogic.values;
      const url = `/internal/app_search/engines/${engineName}/search_settings`;
      try {
        const response = await http.put(url, {
          body: JSON.stringify((0, _utils.removeBoostStateProps)(values.searchSettings))
        });
        (0, _flash_messages.flashSuccessToast)(_constants.UPDATE_SUCCESS_MESSAGE, {
          text: _constants.SUCCESS_CHANGES_MESSAGE
        });
        actions.onSearchSettingsSuccess(response);
      } catch (e) {
        (0, _flash_messages.flashAPIErrors)(e);
        actions.onSearchSettingsError();
      } finally {
        const {
          invalidBoosts,
          unsearchedUnconfirmedFields
        } = _engine.EngineLogic.values.engine;
        if (invalidBoosts || unsearchedUnconfirmedFields) {
          // Re-fetch engine data so that any navigation flags are updated dynamically
          _engine.EngineLogic.actions.initializeEngine();
        }
      }
    },
    resetSearchSettings: async () => {
      if (window.confirm(_constants.RESET_CONFIRMATION_MESSAGE)) {
        const {
          http
        } = _http.HttpLogic.values;
        const {
          engineName
        } = _engine.EngineLogic.values;
        const url = `/internal/app_search/engines/${engineName}/search_settings/reset`;
        try {
          const response = await http.post(url);
          (0, _flash_messages.flashSuccessToast)(_constants.DELETE_SUCCESS_MESSAGE, {
            text: _constants.SUCCESS_CHANGES_MESSAGE
          });
          actions.onSearchSettingsSuccess(response);
        } catch (e) {
          (0, _flash_messages.flashAPIErrors)(e);
          actions.onSearchSettingsError();
        }
      }
    },
    toggleSearchField: ({
      name,
      disableField
    }) => {
      const {
        searchSettings
      } = values;
      const searchFields = disableField ? (0, _lodash.omit)(searchSettings.search_fields, name) : {
        ...searchSettings.search_fields,
        [name]: {
          weight: 1
        }
      };
      actions.setSearchSettings({
        ...searchSettings,
        search_fields: searchFields
      });
    },
    updateFieldWeight: ({
      name,
      weight
    }) => {
      const {
        searchSettings
      } = values;
      const {
        search_fields: searchFields
      } = searchSettings;
      actions.setSearchSettings({
        ...searchSettings,
        search_fields: {
          ...searchFields,
          [name]: {
            ...searchFields[name],
            weight: Math.round(weight * 10) / 10
          }
        }
      });
    },
    addBoost: ({
      name,
      type
    }) => {
      const {
        searchSettings
      } = values;
      const {
        boosts
      } = searchSettings;
      const emptyBoost = _constants.BOOST_TYPE_TO_EMPTY_BOOST[type];
      let boostArray;
      if (Array.isArray(boosts[name])) {
        boostArray = boosts[name].slice();
        boostArray.push(emptyBoost);
      } else {
        boostArray = [emptyBoost];
      }
      actions.setSearchSettings({
        ...searchSettings,
        boosts: {
          ...boosts,
          [name]: boostArray
        }
      });
    },
    deleteBoost: ({
      name,
      index
    }) => {
      if (window.confirm(_constants.DELETE_CONFIRMATION_MESSAGE)) {
        const {
          searchSettings
        } = values;
        const {
          boosts
        } = searchSettings;
        const boostsRemoved = boosts[name].slice();
        boostsRemoved.splice(index, 1);
        const updatedBoosts = {
          ...boosts
        };
        if (boostsRemoved.length > 0) {
          updatedBoosts[name] = boostsRemoved;
        } else {
          delete updatedBoosts[name];
        }
        actions.setSearchSettings({
          ...searchSettings,
          boosts: updatedBoosts
        });
      }
    },
    updateBoostFactor: ({
      name,
      index,
      factor
    }) => {
      const {
        searchSettings
      } = values;
      const {
        boosts
      } = searchSettings;
      const updatedBoosts = (0, _lodash.cloneDeep)(boosts[name]);
      updatedBoosts[index].factor = Math.round(factor * 10) / 10;
      actions.setSearchSettings({
        ...searchSettings,
        boosts: {
          ...boosts,
          [name]: updatedBoosts
        }
      });
    },
    updateBoostValue: ({
      name,
      boostIndex,
      updatedValues
    }) => {
      const {
        searchSettings
      } = values;
      const {
        boosts
      } = searchSettings;
      const updatedBoosts = (0, _lodash.cloneDeep)(boosts[name]);
      updatedBoosts[boostIndex].value = updatedValues;
      actions.setSearchSettings({
        ...searchSettings,
        boosts: {
          ...boosts,
          [name]: updatedBoosts
        }
      });
    },
    updateBoostCenter: ({
      name,
      boostIndex,
      value
    }) => {
      const {
        searchSettings
      } = values;
      const {
        boosts
      } = searchSettings;
      const updatedBoosts = (0, _lodash.cloneDeep)(boosts[name]);
      const fieldType = values.schema[name].type;
      updatedBoosts[boostIndex].center = (0, _utils.parseBoostCenter)(fieldType, value);
      actions.setSearchSettings({
        ...searchSettings,
        boosts: {
          ...boosts,
          [name]: updatedBoosts
        }
      });
    },
    updateBoostSelectOption: ({
      name,
      boostIndex,
      optionType,
      value
    }) => {
      const {
        searchSettings
      } = values;
      const {
        boosts
      } = searchSettings;
      const updatedBoosts = (0, _lodash.cloneDeep)(boosts[name]);
      if (optionType === 'operation') {
        updatedBoosts[boostIndex][optionType] = value;
      } else {
        updatedBoosts[boostIndex][optionType] = value;
      }
      actions.setSearchSettings({
        ...searchSettings,
        boosts: {
          ...boosts,
          [name]: updatedBoosts
        }
      });
    },
    setSearchQuery: () => {
      actions.getSearchResults();
    },
    setPrecision: () => {
      actions.getSearchResults();
    }
  })
});
exports.RelevanceTuningLogic = RelevanceTuningLogic;