"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.mergeServerAndStaticData = exports.fetchSourceStatuses = exports.SourcesLogic = exports.POLLING_INTERVAL = void 0;
var _kea = require("kea");
var _lodash = require("lodash");
var _i18n = require("@kbn/i18n");
var _flash_messages = require("../../../shared/flash_messages");
var _http = require("../../../shared/http");
var _app_logic = require("../../app_logic");
var _utils = require("../../utils");
var _source_data = require("./source_data");
/*
 * 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.
 */

let pollingInterval;
const POLLING_INTERVAL = 10000;
exports.POLLING_INTERVAL = POLLING_INTERVAL;
const SourcesLogic = (0, _kea.kea)({
  path: ['enterprise_search', 'workplace_search', 'sources_logic'],
  actions: {
    setServerSourceStatuses: statuses => statuses,
    onInitializeSources: serverResponse => serverResponse,
    onSetSearchability: (sourceId, searchable) => ({
      sourceId,
      searchable
    }),
    setAddedSource: (addedSourceName, additionalConfiguration, serviceType) => ({
      addedSourceName,
      additionalConfiguration,
      serviceType
    }),
    resetPermissionsModal: () => true,
    resetSourcesState: () => true,
    initializeSources: () => true,
    pollForSourceStatusChanges: () => true,
    setSourceSearchability: (sourceId, searchable) => ({
      sourceId,
      searchable
    })
  },
  reducers: {
    contentSources: [[], {
      onInitializeSources: (_, {
        contentSources
      }) => contentSources,
      onSetSearchability: (contentSources, {
        sourceId,
        searchable
      }) => updateSourcesOnToggle(contentSources, sourceId, searchable)
    }],
    privateContentSources: [[], {
      onInitializeSources: (_, {
        privateContentSources
      }) => privateContentSources || [],
      onSetSearchability: (privateContentSources, {
        sourceId,
        searchable
      }) => updateSourcesOnToggle(privateContentSources, sourceId, searchable)
    }],
    serviceTypes: [[], {
      onInitializeSources: (_, {
        serviceTypes
      }) => serviceTypes || []
    }],
    permissionsModal: [null, {
      setAddedSource: (_, data) => data,
      resetPermissionsModal: () => null
    }],
    dataLoading: [true, {
      onInitializeSources: () => false,
      resetSourcesState: () => true
    }],
    serverStatuses: [null, {
      setServerSourceStatuses: (_, sources) => {
        const serverStatuses = {};
        sources.forEach(source => {
          serverStatuses[source.id] = source.status.status;
        });
        return serverStatuses;
      }
    }]
  },
  selectors: ({
    selectors
  }) => ({
    availableSources: [() => [selectors.sourceData], sourceData => (0, _utils.sortByName)(sourceData.filter(({
      configured,
      serviceType,
      externalConnectorServiceDescribed
    }) => !configured && (serviceType !== 'external' || externalConnectorServiceDescribed)))],
    configuredSources: [() => [selectors.sourceData], sourceData => (0, _utils.sortByName)(sourceData.filter(({
      configured
    }) => configured))],
    externalConfigured: [() => [selectors.configuredSources], configuredSources => !!configuredSources.find(item => item.serviceType === 'external')],
    sourceData: [() => [selectors.serviceTypes, selectors.contentSources], (serviceTypes, contentSources) => mergeServerAndStaticData(serviceTypes, _source_data.staticSourceData, contentSources)]
  }),
  listeners: ({
    actions,
    values
  }) => ({
    initializeSources: async (_, breakpoint) => {
      const {
        isOrganization
      } = _app_logic.AppLogic.values;
      const route = isOrganization ? '/internal/workplace_search/org/sources' : '/internal/workplace_search/account/sources';
      try {
        const response = await _http.HttpLogic.values.http.get(route);
        breakpoint(); // Prevents errors if logic unmounts while fetching
        actions.pollForSourceStatusChanges();
        actions.onInitializeSources(response);
      } catch (e) {
        if ((0, _kea.isBreakpoint)(e)) {
          return; // do not continue if logic is unmounted
        } else {
          (0, _flash_messages.flashAPIErrors)(e);
        }
      }
      if (isOrganization && !values.serverStatuses) {
        // We want to get the initial statuses from the server to compare our polling results to.
        const sourceStatuses = await fetchSourceStatuses(isOrganization, breakpoint);
        actions.setServerSourceStatuses(sourceStatuses !== null && sourceStatuses !== void 0 ? sourceStatuses : []);
      }
    },
    // We poll the server and if the status update, we trigger a new fetch of the sources.
    pollForSourceStatusChanges: (_, breakpoint) => {
      const {
        isOrganization
      } = _app_logic.AppLogic.values;
      if (!isOrganization) return;
      const serverStatuses = values.serverStatuses;
      pollingInterval = window.setInterval(async () => {
        const sourceStatuses = await fetchSourceStatuses(isOrganization, breakpoint);
        (sourceStatuses !== null && sourceStatuses !== void 0 ? sourceStatuses : []).some(source => {
          if (serverStatuses && serverStatuses[source.id] !== source.status.status) {
            return actions.initializeSources();
          }
        });
      }, POLLING_INTERVAL);
    },
    setSourceSearchability: async ({
      sourceId,
      searchable
    }) => {
      const {
        isOrganization
      } = _app_logic.AppLogic.values;
      const route = isOrganization ? `/internal/workplace_search/org/sources/${sourceId}/searchable` : `/internal/workplace_search/account/sources/${sourceId}/searchable`;
      try {
        await _http.HttpLogic.values.http.put(route, {
          body: JSON.stringify({
            searchable
          })
        });
        actions.onSetSearchability(sourceId, searchable);
      } catch (e) {
        (0, _flash_messages.flashAPIErrors)(e);
      }
    },
    setAddedSource: ({
      addedSourceName,
      additionalConfiguration
    }) => {
      const successfullyConnectedMessage = _i18n.i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.flashMessages.contentSourceConnected', {
        defaultMessage: 'Successfully connected {sourceName}.',
        values: {
          sourceName: addedSourceName
        }
      });
      const additionalConfigurationMessage = _i18n.i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.flashMessages.additionalConfigurationNeeded', {
        defaultMessage: 'This source requires additional configuration.'
      });
      (0, _flash_messages.flashSuccessToast)([successfullyConnectedMessage, additionalConfiguration ? additionalConfigurationMessage : ''].join(' '));
    },
    resetSourcesState: () => {
      clearInterval(pollingInterval);
    }
  }),
  events: () => ({
    beforeUnmount() {
      clearInterval(pollingInterval);
    }
  })
});
exports.SourcesLogic = SourcesLogic;
const fetchSourceStatuses = async (isOrganization, breakpoint) => {
  const route = isOrganization ? '/internal/workplace_search/org/sources/status' : '/internal/workplace_search/account/sources/status';
  let response;
  try {
    response = await _http.HttpLogic.values.http.get(route);
    breakpoint();
    SourcesLogic.actions.setServerSourceStatuses(response);
  } catch (e) {
    if ((0, _kea.isBreakpoint)(e)) {
      // Do nothing, silence the error
    } else {
      (0, _flash_messages.flashAPIErrors)(e);
    }
  }
  return response;
};
exports.fetchSourceStatuses = fetchSourceStatuses;
const updateSourcesOnToggle = (contentSources, sourceId, searchable) => {
  const sources = (0, _lodash.cloneDeep)(contentSources);
  const index = (0, _lodash.findIndex)(sources, ({
    id
  }) => id === sourceId);
  const updatedSource = sources[index];
  sources[index] = {
    ...updatedSource,
    searchable
  };
  return sources;
};

/**
 * We have 3 different data sets we have to combine in the UI. The first is the static (`staticSourceData`)
 * data that contains the UI componets, such as the Path for React Router and the copy and images.
 *
 * The second is the base list of available sources that the server sends back in the collection,
 * `availableTypes` that is the source of truth for the name and whether the source has been configured.
 *
 * Finally, also in the collection response is the current set of connected sources. We check for the
 * existence of a `connectedSource` of the type in the loop and set `connected` to true so that the UI
 * can diplay "Add New" instead of "Connect", the latter of which is displated only when a connector
 * has been configured but there are no connected sources yet.
 */
const mergeServerAndStaticData = (serverData, staticData, contentSources) => {
  const unsortedData = staticData.map(staticItem => {
    const serverItem = staticItem.baseServiceType ? undefined // static items with base service types will never have matching external connectors, BE doesn't pass us a baseServiceType
    : serverData.find(({
      serviceType
    }) => serviceType === staticItem.serviceType);
    const connectedSource = contentSources.find(({
      baseServiceType,
      serviceType
    }) => serviceType === staticItem.serviceType && baseServiceType === staticItem.baseServiceType);
    return {
      ...staticItem,
      ...serverItem,
      connected: !!connectedSource
    };
  });
  return (0, _utils.sortByName)(unsortedData);
};
exports.mergeServerAndStaticData = mergeServerAndStaticData;