package org.implab.gradle.variants.artifacts.internal;

import java.util.HashMap;
import java.util.Map;

import org.gradle.api.Action;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
import org.implab.gradle.common.core.lang.FilePaths;
import org.implab.gradle.common.core.lang.Strings;
import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry;
import org.implab.gradle.variants.artifacts.ArtifactSlot;
import org.implab.gradle.variants.artifacts.OutgoingVariant;
import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
import org.implab.gradle.variants.sources.CompileUnitsView;
import org.implab.gradle.variants.sources.RoleProjectionsView;
import org.implab.gradle.variants.sources.SourceSetMaterializer;

public class ArtifactConfigurationHandler {
    private final ArtifactAssemblyRegistry registry;

    private final CompileUnitsView compileUnitsView;

    private final RoleProjectionsView roleProjectionsView;

    private final SourceSetMaterializer sourceSetMaterializer;

    private final Map<ArtifactSlot, SlotContributionVisitor> visitors = new HashMap<>();

    private final ObjectFactory objectFactory;

    private final DirectoryProperty assembliesDirectory;

    private final TaskContainer tasks;

    public ArtifactConfigurationHandler(
            ArtifactAssemblyRegistry registry,
            CompileUnitsView compileUnitsView,
            RoleProjectionsView roleProjectionsView,
            SourceSetMaterializer sourceSetMaterializer,
            ObjectFactory objectFactory,
            TaskContainer tasks) {
        this.registry = registry;
        this.compileUnitsView = compileUnitsView;
        this.roleProjectionsView = roleProjectionsView;
        this.sourceSetMaterializer = sourceSetMaterializer;
        this.objectFactory = objectFactory;
        this.assembliesDirectory = objectFactory.directoryProperty();
        this.tasks = tasks;
    }

    public SlotContributionVisitor slotAssembler(ArtifactSlot artifactSlot) {
        return visitors.computeIfAbsent(artifactSlot, this::newVisitor);
    }

    public void configureVariant(OutgoingVariant outgoingVariant, Action<? super VariantArtifactsSpec> action) {
        var spec = new DefaultVariantArtifactSpec(outgoingVariant, objectFactory, this);

        action.execute(spec);
    }

    private SlotContributionVisitor newVisitor(ArtifactSlot artifactSlot) {
        var fileCollection = objectFactory.fileCollection();
        var outputDirectory = outputDirectory(artifactSlot);

        var task = tasks.register(assembleTaskName(artifactSlot), Copy.class, copy -> {
            copy.setGroup(LifecycleBasePlugin.BUILD_GROUP);
            copy.into(outputDirectory);
            copy.from(fileCollection);
        });

        // регистрируется задача и арефакт сборки слота
        registry.register(artifactSlot, task, t -> outputDirectory);

        return new SlotInputsAssembler(
                artifactSlot,
                fileCollection,
                compileUnitsView,
                roleProjectionsView,
                sourceSetMaterializer);
    }

    private String assembleTaskName(ArtifactSlot artifactSlot) {
        return "assemble"
                + Strings.capitalize(artifactSlot.variant().getName())
                + Strings.capitalize(artifactSlot.slot().getName());
    }

    private Provider<Directory> outputDirectory(ArtifactSlot artifactSlot) {
        return assembliesDirectory.dir(
                FilePaths.cat(
                        artifactSlot.variant().getName(),
                        artifactSlot.slot().getName()));
    }
}
