package org.alien4cloud.alm.deployment.configuration.flow.modifiers.matching;

import static alien4cloud.utils.AlienUtils.safe;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.alien4cloud.alm.deployment.configuration.flow.FlowExecutionContext;
import org.alien4cloud.alm.deployment.configuration.flow.ITopologyModifier;
import org.alien4cloud.alm.deployment.configuration.flow.modifiers.PluginModifierRegistry;
import org.alien4cloud.alm.deployment.configuration.model.DeploymentMatchingConfiguration;
import org.alien4cloud.tosca.model.templates.PolicyTemplate;
import org.alien4cloud.tosca.model.templates.Topology;
import org.alien4cloud.tosca.model.types.PolicyType;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;

import alien4cloud.model.orchestrators.locations.PolicyLocationResourceTemplate;
import alien4cloud.plugin.exception.MissingPluginException;
import alien4cloud.tosca.context.ToscaContext;
import alien4cloud.utils.TagUtil;

/**
 * Last policy matching modifier, it actually inject policy modifiers implementations as policies implementations may impact the actual topology to be deployed.
 */
@Component
public class PolicyMatchingReplaceModifier extends AbstractMatchingReplaceModifier<PolicyTemplate, PolicyLocationResourceTemplate> {
    @Inject
    private PluginModifierRegistry pluginModifierRegistry;

    @Override
    public void process(Topology topology, FlowExecutionContext context) {
        super.process(topology, context);

        // In addition to processing policy template replacements we also inject implementation modifiers for policies that defines them

        for (PolicyTemplate policyTemplate : safe(topology.getPolicies()).values()) {
            PolicyType policyType = ToscaContext.get(PolicyType.class, policyTemplate.getType());
            String policyImplMeta = TagUtil.getTagValue(policyType.getTags(), "a4c_policy_impl");
            if (policyImplMeta == null) {
                context.log().warn("Matched policy {} for {} does not define an alien topology modifier implementation, it may not be taken in account.",
                        policyTemplate.getType(), policyTemplate.getName());
                continue;
            }
            String[] policyImpl = policyImplMeta.split(":");
            if (policyImpl.length != 3) {
                context.log().error(
                        "Matched policy {} for policy {} defines an invalid modifier implementation {}, format should be policy_plugin_id:policy_plugin_bean:injection_phase",
                        policyTemplate.getType(), policyTemplate.getName(), policyImplMeta);
            }

            try {
                ITopologyModifier modifier = pluginModifierRegistry.getPluginBean(policyImpl[0], policyImpl[1]);
                List<ITopologyModifier> phaseModifiers = (List<ITopologyModifier>) context.getExecutionCache().computeIfAbsent(policyImpl[2],
                        s -> Lists.<ITopologyModifier> newArrayList());
                // No need to add a modifier more than once for a phase
                if (!phaseModifiers.contains(modifier)) {
                    phaseModifiers.add(modifier);
                }
            } catch (MissingPluginException e) {
                context.log().error("Implementation specified for policy type {} that refers to plugin bean {}, {} cannot be found.", policyTemplate.getType(),
                        policyImpl[0], policyImpl[1]);
            }
        }
    }

    @Override
    protected String getOriginalTemplateCacheKey() {
        return FlowExecutionContext.MATCHING_ORIGINAL_POLICIES;
    }

    @Override
    protected String getReplacedTemplateCacheKey() {
        return FlowExecutionContext.MATCHING_REPLACED_POLICIES;
    }

    @Override
    protected Map<String, PolicyLocationResourceTemplate> getMatchesById(FlowExecutionContext context) {
        return (Map<String, PolicyLocationResourceTemplate>) context.getExecutionCache().get(FlowExecutionContext.MATCHED_POLICY_LOCATION_TEMPLATES_BY_ID_MAP);
    }

    @Override
    protected Map<String, String> getUserMatches(DeploymentMatchingConfiguration matchingConfiguration) {
        return matchingConfiguration.getMatchedPolicies();
    }

    @Override
    protected Map<String, PolicyTemplate> getTopologyTemplates(Topology topology) {
        return topology.getPolicies();
    }

    @Override
    protected PolicyLocationResourceTemplate getLocationResourceTemplateCopy(String locationResourceTemplateId) {
        return getLocationResourceService().getOrFail(locationResourceTemplateId);
    }

    @Override
    protected void processSpecificReplacement(PolicyTemplate replacingNode, PolicyTemplate replacedTopologyNode, Set<String> topologyNotMergedProps) {
        replacingNode.setTargets(replacedTopologyNode.getTargets());
        replacingNode.setTriggers(replacedTopologyNode.getTriggers());
    }
}
@@ -215,24 +215,85 @@
 ct.Inject;%0A%0A
+import alien4cloud.model.deployment.matching.ILocationMatch;%0A
 import org.a
@@ -1485,16 +1485,622 @@
 istry;%0A%0A
+    /**%0A     * Add locations dependencies%0A     */%0A    @Override%0A    protected void init(Topology topology, FlowExecutionContext context) %7B%0A        List%3CILocationMatch%3E locations = (List%3CILocationMatch%3E) context.getExecutionCache().get(FlowExecutionContext.LOCATION_MATCH_CACHE_KEY);%0A        for (ILocationMatch location : locations) %7B%0A            // FIXME manage conflicting dependencies by fetching types from latest version%0A            topology.getDependencies().addAll(location.getLocation().getDependencies());%0A        %7D%0A        ToscaContext.get().resetDependencies(topology.getDependencies());%0A    %7D%0A%0A
     @Ove

add location dependencies after policy matching