"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getAllRelatedAssets = getAllRelatedAssets;
var _lodash = require("lodash");
var _debug_log = require("../../common/debug_log");
var _get_assets = require("./get_assets");
var _get_indirectly_related_assets = require("./get_indirectly_related_assets");
var _errors = require("./errors");
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.
 */

async function getAllRelatedAssets(elasticsearchClient, options) {
  // How to put size into this?
  const {
    ean,
    from,
    to,
    relation,
    maxDistance,
    kind = []
  } = options;
  const primary = await findPrimary(elasticsearchClient, {
    ean,
    from,
    to
  });
  let assetsToFetch = [primary];
  let currentDistance = 1;
  const relatedAssets = [];
  while (currentDistance <= maxDistance) {
    const queryOptions = {
      relation,
      from,
      to,
      visitedEans: [primary['asset.ean'], ...relatedAssets.map(asset => asset['asset.ean'])]
    };
    // if we enforce the kind filter before the last query we'll miss nodes with
    // possible edges to the requested kind values
    if (currentDistance === maxDistance && kind.length) {
      queryOptions.kind = kind;
    }
    const results = (0, _lodash.flatten)(await Promise.all(assetsToFetch.map(asset => findRelatedAssets(elasticsearchClient, asset, queryOptions))));
    if (results.length === 0) {
      break;
    }
    relatedAssets.push(...results.map(withDistance(currentDistance)));
    assetsToFetch = results;
    currentDistance++;
  }
  return {
    primary,
    [relation]: kind.length ? relatedAssets.filter(asset => asset['asset.kind'] && kind.includes(asset['asset.kind'])) : relatedAssets
  };
}
async function findPrimary(elasticsearchClient, {
  ean,
  from,
  to
}) {
  const primaryResults = await (0, _get_assets.getAssets)({
    elasticsearchClient,
    size: 1,
    filters: {
      ean,
      from,
      to
    }
  });
  if (primaryResults.length === 0) {
    throw new _errors.AssetNotFoundError(ean);
  }
  if (primaryResults.length > 1) {
    throw new Error(`Illegal state: Found more than one asset with the same ean (ean=${ean}).`);
  }
  return primaryResults[0];
}
async function findRelatedAssets(elasticsearchClient, primary, {
  relation,
  from,
  to,
  kind,
  visitedEans
}) {
  const relationField = relationToDirectField(relation);
  const directlyRelatedEans = (0, _utils.toArray)(primary[relationField]);
  (0, _debug_log.debug)('Directly Related EAN values found on primary asset', directlyRelatedEans);
  let directlyRelatedAssets = [];

  // get the directly related assets we haven't visited already
  const remainingEansToFind = (0, _lodash.without)(directlyRelatedEans, ...visitedEans);
  if (remainingEansToFind.length > 0) {
    directlyRelatedAssets = await (0, _get_assets.getAssets)({
      elasticsearchClient,
      filters: {
        ean: remainingEansToFind,
        from,
        to,
        kind
      }
    });
  }
  (0, _debug_log.debug)('Directly related assets found:', JSON.stringify(directlyRelatedAssets));
  const indirectlyRelatedAssets = await (0, _get_indirectly_related_assets.getIndirectlyRelatedAssets)({
    elasticsearchClient,
    ean: primary['asset.ean'],
    excludeEans: visitedEans.concat(directlyRelatedEans),
    relation,
    from,
    to,
    kind
  });
  (0, _debug_log.debug)('Indirectly related assets found:', JSON.stringify(indirectlyRelatedAssets));
  return [...directlyRelatedAssets, ...indirectlyRelatedAssets];
}
function relationToDirectField(relation) {
  if (relation === 'ancestors') {
    return 'asset.parents';
  } else if (relation === 'descendants') {
    return 'asset.children';
  } else {
    return 'asset.references';
  }
}
function withDistance(distance) {
  return asset => ({
    ...asset,
    distance
  });
}