"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports._INTERNAL_FNS = void 0;
exports.resolveDynamicValue = resolveDynamicValue;
exports.resolveDynamicValues = resolveDynamicValues;
exports.resolveItemQueryValues = resolveItemQueryValues;
const get_prop_1 = require("../../objects/get-prop");
const resolvePortalValues_1 = require("./resolvePortalValues");
const resolveServiceQueryValues_1 = require("./resolveServiceQueryValues");
const set_prop_1 = require("../../objects/set-prop");
const combineQueries_1 = require("../../search/combineQueries");
const portalSearchItems_1 = require("../../search/_internal/portalSearchItems");
const aggregateValues_1 = require("./aggregateValues");
const memoize_1 = require("../memoize");
exports._INTERNAL_FNS = {
    resolveDynamicValue,
    resolveDynamicValues,
    resolveItemQueryValues,
};
/**
 * @internal
 * Resolve a single dynamic value. The definition can be of many types (item-query, portal etc), each of which has their own logic for resolving the value.
 * @param valueDef
 * @param context
 * @returns
 */
async function resolveDynamicValue(valueDef, context) {
    let result = {};
    switch (valueDef.type) {
        case "static-value": {
            const val = (0, get_prop_1.getProp)(valueDef, "value");
            const outPath = (0, get_prop_1.getProp)(valueDef, "outPath");
            result[outPath] = val;
            break;
        }
        case "item-query": {
            result = await exports._INTERNAL_FNS.resolveItemQueryValues(valueDef, context);
            break;
        }
        case "portal": {
            result = await (0, resolvePortalValues_1.resolvePortalValues)(valueDef, context);
            break;
        }
        case "service-query": {
            result = await (0, resolveServiceQueryValues_1.resolveServiceQueryValues)(valueDef, context);
            break;
        }
        default: {
            throw new Error(`Cannot resolve value - unexpected source.`);
        }
    }
    return result;
}
/**
 * @internal
 * Resolve a set of dynamic values.
 * The individual definitions can be of many types (item-query, portal etc), each of which has their own logic for resolving the value.
 * @param dynamicValues Array of dynamic value definitions to resolve
 * @param context ArcGIS context of the current user.
 * @returns
 */
async function resolveDynamicValues(dynamicValues, context) {
    let result = {};
    // iterate over the dynamic values and resolve each one via Promise.all
    // this will allow us to resolve all the values in parallel
    const resolvedValues = await Promise.all(dynamicValues.map((valueDef) => exports._INTERNAL_FNS.resolveDynamicValue(valueDef, context)));
    // merge the resolved values into a single object
    result = resolvedValues.reduce((acc, val) => (Object.assign(Object.assign({}, acc), val)), {});
    return result;
}
/**
 * @internal
 * Resolve a set of dynamic values based on an item query.
 * This is used to query for a set of items, extract a specific value from each
 * then run an aggregation on the set of values.
 * @param valueDef Item Query Definition that will be resolved by querying for items and extracting a specific value from each
 * @param context ArcGIS context of the current user.
 * @returns
 */
async function resolveItemQueryValues(valueDef, context) {
    const result = {};
    // execute the query
    // TODO: Error if neither query or scope are defined
    // Combine the query and scope into a single query;
    // The query and scope have already been dereferenced
    // but since the typing allows for `Reference` we need
    // to use `as IQuery` to TS to chill out.
    const query = valueDef.query || {
        targetEntity: "item",
        filters: [],
    };
    const scope = valueDef.scope || {
        targetEntity: "item",
        filters: [],
    };
    const combined = (0, combineQueries_1.combineQueries)([query, scope]);
    const opts = {
        requestOptions: context.hubRequestOptions,
    };
    // create/get memoized version of portalSearchItemsAsItems
    const memoizedPortalSearch = (0, memoize_1.memoize)(portalSearchItems_1.portalSearchItemsAsItems);
    // Execute the query
    const response = await memoizedPortalSearch(combined, opts);
    // TODO: dereference metrics so we can delegate one to another
    // This next section is all promise based so that a dynamic value
    // can itself be a dynamic value definition, which then needs to be
    // resolved.
    const promises = await response.results.reduce(async (valsPromise, r) => {
        const vals = await valsPromise;
        const valueFromItem = (0, get_prop_1.getProp)(r, valueDef.sourcePath);
        // Handle case where value is not found...
        // e.g. the project exists but the metric is not defined yet
        if (valueFromItem) {
            if (typeof valueFromItem === "string" ||
                typeof valueFromItem === "number") {
                vals.push(Promise.resolve(valueFromItem));
            }
            else if (valueFromItem.type === "static-value") {
                vals.push(Promise.resolve(valueFromItem.value));
            }
            else {
                // valueFromItem is itself a valueDefinition so we call resolveDynamicValues again
                const vResult = await exports._INTERNAL_FNS.resolveDynamicValue(valueFromItem, context);
                // vals needs just the value - the number or string but vResult is an object
                // so we need to get it out of the object
                vals.push((0, get_prop_1.getProp)(vResult, valueFromItem.outPath));
            }
        }
        return vals;
    }, Promise.resolve([]));
    const values = await Promise.all(promises);
    const aggretate = (0, aggregateValues_1.aggregateValues)(values, valueDef.aggregation);
    (0, set_prop_1.setProp)(valueDef.outPath, aggretate, result);
    return result;
}
//# sourceMappingURL=resolveDynamicValues.js.map