"use strict";
/*
 * Copyright © 2019 Atomist, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.enrichGoalSetters = exports.goalContributors = exports.attachFacts = void 0;
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const _ = require("lodash");
const sha_1 = require("../../api-helper/misc/sha");
const Locking_1 = require("../goal/common/Locking");
const Goals_1 = require("../goal/Goals");
const GoalSetter_1 = require("../mapping/GoalSetter");
const Mapping_1 = require("../mapping/Mapping");
const GoalComponent_1 = require("./GoalComponent");
/**
 * Enrich the invocation, attaching some facts.
 * The returned object will be merged with any facts already on the invocation.
 * @param {(f: (StatefulInvocation<FACT>)) => Promise<FACT>} compute additional facts.
 * @return {GoalContribution<F>}
 */
function attachFacts(compute) {
    return {
        name: "attachFacts-" + sha_1.computeShaOf(compute.toString()),
        mapping: async (f) => {
            const withAdditionalFact = f;
            if (!withAdditionalFact.facts) {
                withAdditionalFact.facts = {};
            }
            const additionalState = await compute(withAdditionalFact);
            _.merge(withAdditionalFact.facts, additionalState);
            // The GoalContribution itself will be ignored
            return undefined;
        },
    };
}
exports.attachFacts = attachFacts;
/**
 * An additive goal setter assembles the goals contributed by all the contributors.
 */
class AdditiveGoalSetter {
    constructor(name, contributors) {
        this.name = name;
        this.contributors = contributors;
    }
    get label() {
        return this.contributors.filter(c => c.label)
            .map(c => c.label).join(", ");
    }
    get structure() {
        return {
            components: this.contributors.map(vague => Mapping_1.mapMapping(vague, GoalComponent_1.toGoals)),
            compositionStyle: GoalSetter_1.GoalSettingCompositionStyle.AllMatches,
        };
    }
    async mapping(p) {
        const names = [];
        const contributorGoals = [];
        for (const c of this.contributors) {
            const mapping = await c.mapping(p);
            if (mapping) {
                const goals = GoalComponent_1.toGoals(mapping);
                if (c.label) {
                    names.push(c.label);
                }
                else {
                    names.push(c.name);
                }
                contributorGoals.push(goals.goals.filter(g => g !== Locking_1.Locking));
                // If we find the special locking goal, don't add any further goals
                if (goals.goals.includes(Locking_1.Locking)) {
                    logger_1.logger.debug("Stopping goal contribution analysis, because %s has locked the goal set", c.name);
                    break;
                }
            }
        }
        const uniqueGoals = _.uniq(_.flatten(contributorGoals.filter(x => !!x)));
        logger_1.logger.debug("%d contributors (%s): Contributor goal names=[%s]; Unique goal names=[%s]; correlationId=%s", this.contributors.length, this.contributors.map(c => c.name), contributorGoals.map(a => !!a ? a.map(b => b.name).join() : "undefined").join(": "), uniqueGoals.map(g => g.name), p.context.correlationId);
        return uniqueGoals.length === 0 ?
            undefined :
            new Goals_1.Goals(names.join(", "), ...uniqueGoals);
    }
}
/**
 * Contribute goals based on a series of contribution rules.
 *
 * Instead of stopping at the first match, each push will get _all_ the goals it qualifies for.
 *
 * Duplicates will be removed.
 *
 * @param contributor first contributor
 * @param {GoalContribution<F>} contributors
 * @return a mapping to goals
 */
function goalContributors(contributor, ...contributors) {
    if (contributors.length === 0) {
        return Mapping_1.mapMapping(contributor, GoalComponent_1.toGoals);
    }
    return enrichGoalSetters(contributor, contributors[0], ...contributors.slice(1));
}
exports.goalContributors = goalContributors;
/**
 * Enrich the given push mapping with our own contributions
 * @param {Mapping<F extends SdmContext, Goals>} mapping
 * @param {GoalContribution<F extends SdmContext>} contributor
 * @param {GoalContribution<F extends SdmContext>} contributors
 * @return {Mapping<F extends SdmContext, Goals>}
 */
function enrichGoalSetters(mapping, contributor, ...contributors) {
    if (isAdditiveGoalSetter(mapping)) {
        return new AdditiveGoalSetter(`${mapping.name}-enriched`, [...mapping.contributors, contributor, ...contributors]);
    }
    return new AdditiveGoalSetter(`${mapping.name}-enriched`, [mapping, contributor].concat(contributors));
}
exports.enrichGoalSetters = enrichGoalSetters;
function isAdditiveGoalSetter(a) {
    const maybe = a;
    return !!maybe && !!maybe.contributors && !!maybe.mapping;
}
//# sourceMappingURL=goalContribution.js.map