<commit_before>package net.nemerosa.ontrack.service;

import com.fasterxml.jackson.databind.JsonNode;
import net.nemerosa.ontrack.model.security.BranchEdit;
import net.nemerosa.ontrack.model.security.SecurityService;
import net.nemerosa.ontrack.model.structure.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class CopyServiceImpl implements CopyService {

    private final StructureService structureService;
    private final PropertyService propertyService;
    private final SecurityService securityService;

    @Autowired
    public CopyServiceImpl(StructureService structureService, PropertyService propertyService, SecurityService securityService) {
        this.structureService = structureService;
        this.propertyService = propertyService;
        this.securityService = securityService;
    }

    @Override
    public Branch copy(Branch targetBranch, BranchCopyRequest request) {
        // Gets the source branch
        Branch sourceBranch = structureService.getBranch(request.getSourceBranchId());
        // If same branch, rejects
        if (sourceBranch.id() == targetBranch.id()) {
            throw new IllegalArgumentException("Cannot copy the branch into itself.");
        }
        // Checks the rights on the target branch
        securityService.checkProjectFunction(targetBranch, BranchEdit.class);
        // Now, we can work in a secure context
        securityService.asAdmin(() -> doCopy(sourceBranch, targetBranch, request));
        return targetBranch;
    }

    protected void doCopy(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        // Same project?
        boolean sameProject = (sourceBranch.projectId() == targetBranch.projectId());
        // TODO Branch properties
        // Promotion level and properties
        doCopyPromotionLevels(sourceBranch, targetBranch, request);
        // Validation stamps and properties
        doCopyValidationStamps(sourceBranch, targetBranch, request);
        // TODO User filters
    }

    protected void doCopyPromotionLevels(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        List<PromotionLevel> sourcePromotionLevels = structureService.getPromotionLevelListForBranch(sourceBranch.getId());
        for (PromotionLevel sourcePromotionLevel : sourcePromotionLevels) {
            Optional<PromotionLevel> targetPromotionLevelOpt = structureService.findPromotionLevelByName(targetBranch.getProject().getName(), targetBranch.getName(), sourcePromotionLevel.getName());
            if (!targetPromotionLevelOpt.isPresent()) {
                // Copy of the promotion level
                PromotionLevel targetPromotionLevel = structureService.newPromotionLevel(
                        PromotionLevel.of(
                                targetBranch,
                                NameDescription.nd(
                                        sourcePromotionLevel.getName(),
                                        applyReplacements(sourcePromotionLevel.getDescription(), request.getPromotionLevelReplacements())
                                )
                        )
                );
                // Copy of the image
                Document image = structureService.getPromotionLevelImage(sourcePromotionLevel.getId());
                if (image != null) {
                    structureService.setPromotionLevelImage(targetPromotionLevel.getId(), image);
                }
                // Copy of properties
                // Gets the properties of the source promotion level
                List<Property<?>> properties = propertyService.getProperties(sourcePromotionLevel);
                for (Property<?> property : properties) {
                    doCopyProperty(property, targetPromotionLevel, request.getPromotionLevelReplacements());
                }
            }
        }
    }

    protected void doCopyValidationStamps(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        List<ValidationStamp> sourceValidationStamps = structureService.getValidationStampListForBranch(sourceBranch.getId());
        for (ValidationStamp sourceValidationStamp : sourceValidationStamps) {
            Optional<ValidationStamp> targetValidationStampOpt = structureService.findValidationStampByName(targetBranch.getProject().getName(), targetBranch.getName(), sourceValidationStamp.getName());
            if (!targetValidationStampOpt.isPresent()) {
                // Copy of the validation stamp
                ValidationStamp targetValidationStamp = structureService.newValidationStamp(
                        ValidationStamp.of(
                                targetBranch,
                                NameDescription.nd(
                                        sourceValidationStamp.getName(),
                                        applyReplacements(sourceValidationStamp.getDescription(), request.getValidationStampReplacements())
                                )
                        )
                );
                // Copy of the image
                Document image = structureService.getValidationStampImage(sourceValidationStamp.getId());
                if (image != null) {
                    structureService.setValidationStampImage(targetValidationStamp.getId(), image);
                }
                // Copy of properties
                // Gets the properties of the source promotion level
                List<Property<?>> properties = propertyService.getProperties(sourceValidationStamp);
                for (Property<?> property : properties) {
                    doCopyProperty(property, targetValidationStamp, request.getValidationStampReplacements());
                }
            }
        }
    }

    protected <T> void doCopyProperty(Property<T> property, ProjectEntity targetEntity, List<Replacement> replacements) {
        if (!property.isEmpty()) {
            // Property value replacement
            T data = property.getType().replaceValue(property.getValue(), s -> applyReplacements(s, replacements));
            // Property data
            JsonNode jsonData = property.getType().forStorage(data);
            // Creates the property
            propertyService.editProperty(
                    targetEntity,<commit_msg>#39 Copy of branch properties<commit_after>package net.nemerosa.ontrack.service;

import com.fasterxml.jackson.databind.JsonNode;
import net.nemerosa.ontrack.model.security.BranchEdit;
import net.nemerosa.ontrack.model.security.SecurityService;
import net.nemerosa.ontrack.model.structure.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class CopyServiceImpl implements CopyService {

    private final StructureService structureService;
    private final PropertyService propertyService;
    private final SecurityService securityService;

    @Autowired
    public CopyServiceImpl(StructureService structureService, PropertyService propertyService, SecurityService securityService) {
        this.structureService = structureService;
        this.propertyService = propertyService;
        this.securityService = securityService;
    }

    @Override
    public Branch copy(Branch targetBranch, BranchCopyRequest request) {
        // Gets the source branch
        Branch sourceBranch = structureService.getBranch(request.getSourceBranchId());
        // If same branch, rejects
        if (sourceBranch.id() == targetBranch.id()) {
            throw new IllegalArgumentException("Cannot copy the branch into itself.");
        }
        // Checks the rights on the target branch
        securityService.checkProjectFunction(targetBranch, BranchEdit.class);
        // Now, we can work in a secure context
        securityService.asAdmin(() -> doCopy(sourceBranch, targetBranch, request));
        return targetBranch;
    }

    protected void doCopy(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        // Branch properties
        doCopyProperties(sourceBranch, targetBranch, request.getPropertyReplacements());
        // Promotion level and properties
        doCopyPromotionLevels(sourceBranch, targetBranch, request);
        // Validation stamps and properties
        doCopyValidationStamps(sourceBranch, targetBranch, request);
        // TODO User filters
    }

    protected void doCopyPromotionLevels(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        List<PromotionLevel> sourcePromotionLevels = structureService.getPromotionLevelListForBranch(sourceBranch.getId());
        for (PromotionLevel sourcePromotionLevel : sourcePromotionLevels) {
            Optional<PromotionLevel> targetPromotionLevelOpt = structureService.findPromotionLevelByName(targetBranch.getProject().getName(), targetBranch.getName(), sourcePromotionLevel.getName());
            if (!targetPromotionLevelOpt.isPresent()) {
                // Copy of the promotion level
                PromotionLevel targetPromotionLevel = structureService.newPromotionLevel(
                        PromotionLevel.of(
                                targetBranch,
                                NameDescription.nd(
                                        sourcePromotionLevel.getName(),
                                        applyReplacements(sourcePromotionLevel.getDescription(), request.getPromotionLevelReplacements())
                                )
                        )
                );
                // Copy of the image
                Document image = structureService.getPromotionLevelImage(sourcePromotionLevel.getId());
                if (image != null) {
                    structureService.setPromotionLevelImage(targetPromotionLevel.getId(), image);
                }
                // Copy of properties
                doCopyProperties(sourcePromotionLevel, targetPromotionLevel, request.getPromotionLevelReplacements());
            }
        }
    }

    protected void doCopyProperties(ProjectEntity source, ProjectEntity target, List<Replacement> replacements) {
        List<Property<?>> properties = propertyService.getProperties(source);
        for (Property<?> property : properties) {
            doCopyProperty(property, target, replacements);
        }
    }

    protected void doCopyValidationStamps(Branch sourceBranch, Branch targetBranch, BranchCopyRequest request) {
        List<ValidationStamp> sourceValidationStamps = structureService.getValidationStampListForBranch(sourceBranch.getId());
        for (ValidationStamp sourceValidationStamp : sourceValidationStamps) {
            Optional<ValidationStamp> targetValidationStampOpt = structureService.findValidationStampByName(targetBranch.getProject().getName(), targetBranch.getName(), sourceValidationStamp.getName());
            if (!targetValidationStampOpt.isPresent()) {
                // Copy of the validation stamp
                ValidationStamp targetValidationStamp = structureService.newValidationStamp(
                        ValidationStamp.of(
                                targetBranch,
                                NameDescription.nd(
                                        sourceValidationStamp.getName(),
                                        applyReplacements(sourceValidationStamp.getDescription(), request.getValidationStampReplacements())
                                )
                        )
                );
                // Copy of the image
                Document image = structureService.getValidationStampImage(sourceValidationStamp.getId());
                if (image != null) {
                    structureService.setValidationStampImage(targetValidationStamp.getId(), image);
                }
                // Copy of properties
                doCopyProperties(sourceValidationStamp, targetValidationStamp, request.getValidationStampReplacements());
            }
        }
    }

    protected <T> void doCopyProperty(Property<T> property, ProjectEntity targetEntity, List<Replacement> replacements) {
        if (!property.isEmpty()) {
            // Property value replacement
            T data = property.getType().replaceValue(property.getValue(), s -> applyReplacements(s, replacements));
            // Property data
            JsonNode jsonData = property.getType().forStorage(data);
            // Creates the property
            propertyService.editProperty(
                    targetEntity,
{'fraction_non_alphanumeric': 0.07509099541066624, 'fraction_numerical': 0.0001582528881152081, 'mean_word_length': 2.422420796100731, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3143771', 'n_tokens_mistral': 2776, 'n_tokens_neox': 2755, 'n_words': 595}