"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.searchSources = searchSources;
exports.searchSource = searchSource;
loader.lazyRequireGetter(this, "_asyncValue", "devtools/client/debugger/src/utils/async-value");
loader.lazyRequireGetter(this, "_index", "devtools/client/debugger/src/selectors/index");
loader.lazyRequireGetter(this, "_location", "devtools/client/debugger/src/utils/location");
loader.lazyRequireGetter(this, "_source", "devtools/client/debugger/src/utils/source");
loader.lazyRequireGetter(this, "_loadSourceText", "devtools/client/debugger/src/actions/sources/loadSourceText");
loader.lazyRequireGetter(this, "_constants", "devtools/client/debugger/src/constants");

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

/**
 * Redux actions for the search state
 *
 * @module actions/search
 */
function searchSources(query, onUpdatedResults, signal) {
  return async ({
    dispatch,
    getState,
    searchWorker
  }) => {
    dispatch({
      type: "SET_PROJECT_SEARCH_QUERY",
      query
    });
    const searchOptions = (0, _index.getSearchOptions)(getState(), _constants.searchKeys.PROJECT_SEARCH);
    const validSources = (0, _index.getSourceList)(getState()).filter(source => !(0, _index.isSourceBlackBoxed)(getState(), source) && !(0, _source.matchesGlobPatterns)(source, searchOptions.excludePatterns)); // Sort original entries first so that search results are more useful.
    // Deprioritize third-party scripts, so their results show last.

    validSources.sort((a, b) => {
      function isThirdParty(source) {
        return source?.url && (source.url.includes("node_modules") || source.url.includes("bower_components"));
      }

      if (a.isOriginal && !isThirdParty(a)) {
        return -1;
      }

      if (b.isOriginal && !isThirdParty(b)) {
        return 1;
      }

      if (!isThirdParty(a) && isThirdParty(b)) {
        return -1;
      }

      if (isThirdParty(a) && !isThirdParty(b)) {
        return 1;
      }

      return 0;
    });
    const results = [];

    for (const source of validSources) {
      const sourceActor = (0, _index.getFirstSourceActorForGeneratedSource)(getState(), source.id);
      await dispatch((0, _loadSourceText.loadSourceText)(source, sourceActor)); // This is the only asynchronous call in this method.
      // We may have stopped the search by closing the search panel or changing the query.
      // Avoid any further unecessary computation when the React Component tells us the query was cancelled.

      if (signal.aborted) {
        return;
      }

      const result = await searchSource(source, sourceActor, query, {
        getState,
        searchWorker
      });

      if (signal.aborted) {
        return;
      }

      if (result) {
        results.push(result);
        onUpdatedResults(results, false, signal);
      }
    }

    onUpdatedResults(results, true, signal);
  };
}

async function searchSource(source, sourceActor, query, {
  getState,
  searchWorker
}) {
  const state = getState();
  const location = (0, _location.createLocation)({
    source,
    sourceActor
  });
  const content = (0, _index.getSettledSourceTextContent)(state, location);
  let matches = [];

  if (content && (0, _asyncValue.isFulfilled)(content) && content.value.type === "text") {
    const options = (0, _index.getSearchOptions)(state, _constants.searchKeys.PROJECT_SEARCH);
    matches = await searchWorker.findSourceMatches(content.value, query, options);
  }

  if (!matches.length) {
    return null;
  }

  return {
    type: "RESULT",
    location,
    // `matches` are generated by project-search worker's `findSourceMatches` method
    matches: matches.map(m => ({
      type: "MATCH",
      location: (0, _location.createLocation)({ ...location,
        // `matches` only contain line and column
        // `location` will already refer to the right source/sourceActor
        line: m.line,
        column: m.column
      }),
      matchIndex: m.matchIndex,
      match: m.match,
      value: m.value
    }))
  };
}