package com.r35157.nenjim.service.journal.model;

import com.r35157.nenjim.service.journal.valuetypes.DependencyTarget;
import com.r35157.nenjim.service.journal.valuetypes.VersionExpressionSet;

import java.util.List;
import java.util.Objects;

/** The complete version contract for one release-to-artifact relationship. */
public record ArtifactDependency(
        DependencyTarget target,
        VersionExpressionSet dependsOn,
        List<PolicyRule> exclusions,
        List<PolicyRule> preferences) {
    public ArtifactDependency {
        Objects.requireNonNull(target, "target");
        Objects.requireNonNull(dependsOn, "dependsOn");
        if (dependsOn.isEmpty()) {
            throw new IllegalArgumentException("DEPENDS_ON cannot be empty");
        }
        exclusions = copyRules(exclusions, "exclusions");
        preferences = copyRules(preferences, "preferences");

        VersionExpressionSet excluded = union(exclusions);
        VersionExpressionSet preferred = union(preferences);
        VersionExpressionSet permitted = dependsOn.subtract(excluded);
        if (!permitted.containsAll(preferred)) {
            throw new IllegalArgumentException(
                    "PREFERRED versions for " + target + " must be a subset of DEPENDS_ON minus EXCLUDES");
        }
    }

    public VersionExpressionSet excludedVersions() {
        return union(exclusions);
    }

    public VersionExpressionSet preferredVersions() {
        return union(preferences);
    }

    public VersionExpressionSet permittedVersions() {
        return dependsOn.subtract(excludedVersions());
    }

    private static List<PolicyRule> copyRules(List<PolicyRule> rules, String name) {
        Objects.requireNonNull(rules, name);
        rules.forEach(rule -> Objects.requireNonNull(rule, name + " rule"));
        return List.copyOf(rules);
    }

    private static VersionExpressionSet union(List<PolicyRule> rules) {
        VersionExpressionSet result = VersionExpressionSet.empty();
        for (PolicyRule rule : rules) result = result.union(rule.versions());
        return result;
    }
}
