/*
 * This file is part of the Meeds project (https://meeds.io/).
 * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package io.meeds.tenant.provisioning.service;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.RootContainer;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.services.security.Authenticator;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.Identity;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.space.spi.SpaceService;
import org.exoplatform.task.domain.Priority;
import org.exoplatform.task.dto.CommentDto;
import org.exoplatform.task.dto.LabelDto;
import org.exoplatform.task.dto.ProjectDto;
import org.exoplatform.task.dto.StatusDto;
import org.exoplatform.task.dto.TaskDto;
import org.exoplatform.task.exception.EntityNotFoundException;
import org.exoplatform.task.service.CommentService;
import org.exoplatform.task.service.LabelService;
import org.exoplatform.task.service.ProjectService;
import org.exoplatform.task.service.StatusService;
import org.exoplatform.task.service.TaskService;

import io.meeds.deeds.blockchain.BlockchainConfigurationProperties;
import io.meeds.deeds.constant.DeedCard;
import io.meeds.deeds.constant.DeedCity;
import io.meeds.deeds.elasticsearch.model.DeedTenant;
import io.meeds.tenant.provisioning.TenantProvisioningConfiguration;

@Component
public class TaskIntegrationService {

  public static final String                TASK_ID_PROP_NAME         = "taskId";

  public static final String                CURRENT_TASK_ID_PROP_NAME = "currentTaskId";

  public static final String                TASK_IDS_PROP_NAME        = "taskIds";

  public static final String                SPACES_PARENT_GROUP       = "/spaces/";

  public static final String                TASK_ID_SEPARATOR         = ":";

  private static final Logger               LOG                       = LoggerFactory.getLogger(TaskIntegrationService.class);

  @Autowired
  private TenantProvisioningConfiguration   provisioningConfiguration;

  @Autowired
  private BlockchainConfigurationProperties blockchainProperties;

  private ProjectService                    taskProjectService;

  private TaskService                       taskService;

  private LabelService                      taskLabelService;

  private CommentService                    taskCommentService;

  private SpaceService                      spaceService;

  private ProjectDto                        project;

  private StatusDto                         taskPendingStatus;

  private StatusDto                         taskConfirmedStatus;

  private LabelDto                          transactionInProgressLabel;

  private LabelDto                          transactionConfirmedLabel;

  private LabelDto                          transactionErrorLabel;

  private LabelDto                          transactionConflictLabel;

  private Identity                          userIdentity;

  private PortalContainer                   container;

  private String                            etherscanPrefix;

  private boolean                           enabled                   = true;

  public boolean saveTask(DeedTenant deedTenant, String transactionHash, boolean start, boolean confirmed) {
    initTaskServices();
    if (!this.enabled) {
      return false;
    }
    // Workaround for TaskService which relies on ConversationState to retrieve
    // username instead of parameter
    ConversationState currentConversationState = setCurrentConversationState();
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    TaskDto taskDto = null;
    try {
      taskDto = getTask(deedTenant, transactionHash, true);
      if (!confirmed && taskDto != null) {
        // Task already exists with confirmed status, thus ignore it
        return false;
      }
      long previousTaskId = getCurrentTaskId(deedTenant);

      StatusDto taskStatus = getTaskStatus(confirmed);
      if (taskDto == null) {
        taskDto = new TaskDto();
        taskDto.setStatus(taskStatus);
        taskDto.setPriority(confirmed ? Priority.HIGH : Priority.LOW);
        taskDto.setRank(1);
        taskDto.setCreatedBy(provisioningConfiguration.getTaskUsername());
        taskDto.setWatcher(getProjectManagers(project.getId()));
        taskDto.setDescription(buildDescription(deedTenant, transactionHash));
        taskDto.setTitle(buildTitle(deedTenant, start, confirmed, false));
        taskDto.setStartDate(new Date());
        taskDto = taskService.createTask(taskDto);
        adTaskLabel(taskDto, start, confirmed);
      } else {
        // If status has changed comparing to original one,
        // The Task will not be moved
        if (taskDto.getStatus() == null || taskPendingStatus.getId().equals(taskDto.getStatus().getId())) {
          taskDto.setStatus(taskStatus);
        }
        taskDto.setTitle(buildTitle(deedTenant, start, confirmed, false));
        taskDto.setPriority(Priority.HIGH);
        taskDto = taskService.updateTask(taskDto);
        adTaskLabel(taskDto, start, confirmed);
      }

      long taskId = taskDto.getId();
      if (previousTaskId > 0 && taskId != previousTaskId) {
        cancelPreviousTask(previousTaskId, taskId, transactionHash);
      }
      return true;
    } finally {
      setTaskInformation(deedTenant, taskDto, start, confirmed, transactionHash);

      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }
      ConversationState.setCurrent(currentConversationState);
    }
  }

  public boolean cancelTask(DeedTenant deedTenant, String transactionHash) {
    initTaskServices();
    if (!this.enabled) {
      return false;
    }
    // Workaround for TaskService which relies on ConversationState to retrieve
    // username instead of parameter
    ConversationState currentConversationState = setCurrentConversationState();
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      TaskDto task = getTask(deedTenant, transactionHash);
      if (task == null) {
        return false;
      } else {
        boolean isStartupCommand = StringUtils.equalsIgnoreCase(transactionHash, deedTenant.getStartupTransactionHash());
        boolean isShutdownCommand = StringUtils.equalsIgnoreCase(transactionHash, deedTenant.getShutdownTransactionHash());
        if (!isStartupCommand || !isShutdownCommand) {
          LOG.warn("Transaction {} isn't found in DEED {} as startup neither shutdown commands. Mark task {} as canceled",
                   transactionHash,
                   deedTenant.getNftId(),
                   task.getId());
          addComment(task,
                     "Transaction not marked as last tracked transaction, thus it has been marked as canceled. Please verify other duplicated tasks for this DEED.");
          addTaskLabel(task, transactionConflictLabel);
        } else {
          addComment(task, "Tenant transaction error - Cancelled");
          addTaskLabel(task, transactionErrorLabel);
        }
        removeTaskLabel(task, transactionInProgressLabel);

        if (!task.isCompleted()) {
          task.setCompleted(true);
          task.setPriority(Priority.NORMAL);
          task.setTitle(buildTitle(deedTenant, isStartupCommand, true, true));
          task = taskService.updateTask(task);
        }
        return true;
      }
    } catch (Exception e) {
      LOG.warn("Error while canceling Provisioning Task for DEED {}", deedTenant, e);
      return false;
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }
      ConversationState.setCurrent(currentConversationState);
    }
  }

  public Set<String> getProjectManagers() {
    initTaskServices();
    if (!this.enabled) {
      return Collections.emptySet();
    }
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      return getProjectManagers(project.getId());
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }
    }
  }

  private void initTaskServices() {
    if (project != null) {
      return;
    }
    container = PortalContainer.getInstance();
    taskProjectService = container.getComponentInstanceOfType(ProjectService.class);
    if (taskProjectService == null) {
      this.enabled = false;
      return;
    }
    taskService = container.getComponentInstanceOfType(TaskService.class);
    taskLabelService = container.getComponentInstanceOfType(LabelService.class);
    taskCommentService = container.getComponentInstanceOfType(CommentService.class);
    spaceService = container.getComponentInstanceOfType(SpaceService.class);

    StatusService taskStatusService = container.getComponentInstanceOfType(StatusService.class);
    Authenticator authenticator = container.getComponentInstanceOfType(Authenticator.class);

    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      String username = provisioningConfiguration.getTaskUsername();
      long taskProjectId = provisioningConfiguration.getTaskProjectId();

      this.userIdentity = authenticator.createIdentity(username);
      if (userIdentity == null) {
        throw new IllegalStateException("User with name " + username + " not found");
      }
      this.project = taskProjectService.getProject(taskProjectId);
      if (project == null) {
        throw new IllegalStateException("Project with id " + taskProjectId + " not found");
      }
      List<StatusDto> statuses = taskStatusService.getStatuses(taskProjectId);
      this.taskPendingStatus = statuses.get(provisioningConfiguration.getTaskStatusPendingIndex());
      this.taskConfirmedStatus = statuses.get(provisioningConfiguration.getTaskStatusConfirmedIndex());
      this.transactionInProgressLabel = getOrCreateLabel(provisioningConfiguration.getTransactionInProgressLabel(), "green");
      this.transactionConfirmedLabel = getOrCreateLabel(provisioningConfiguration.getTransactionConfirmedLabel(), "red");
      this.transactionErrorLabel = getOrCreateLabel(provisioningConfiguration.getTransactionErrorLabel(), "orange");
      this.transactionConflictLabel = getOrCreateLabel(provisioningConfiguration.getTransactionConflictLabel(), "red");
    } catch (Exception e) {
      this.project = null;
      LOG.warn("Error getting Tasks labels", e);
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }
    }
  }

  private void setTaskInformation(DeedTenant deedTenant, TaskDto taskDto, boolean start, boolean confirmed, String transactionHash) {
    if (taskDto != null && taskDto.getId() > 0) {
      setDeedProvisioningTaskId(deedTenant, transactionHash, taskDto.getId());
      if (start) {
        if (confirmed) {
          addComment(taskDto, "Deploy Tenant transaction validated");
        } else {
          addComment(taskDto, "Deploy Tenant transaction in progress");
        }
      } else {
        if (confirmed) {
          addComment(taskDto, "Undeploy Tenant transaction validated");
        } else {
          addComment(taskDto, "Undeploy Tenant transaction in progress");
        }
      }
    }
  }

  private TaskDto getTask(DeedTenant deedTenant, String transactionHash) throws EntityNotFoundException {
    if (!this.enabled) {
      return null;
    }
    // Workaround for TaskService which relies on ConversationState to retrieve
    // username instead of parameter
    ConversationState currentConversationState = setCurrentConversationState();
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      if (deedTenant.getProperties() != null) {
        String taskId = deedTenant.getProperties().get(TASK_ID_PROP_NAME);
        if (StringUtils.isNotBlank(taskId)) {
          String[] taskIdParts = StringUtils.split(taskId, TASK_ID_SEPARATOR);
          if (StringUtils.equalsIgnoreCase(taskIdParts[0], transactionHash)) {
            long id = Long.parseLong(taskIdParts[1]);
            return taskService.getTask(id);
          }
        }
      }
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }

      ConversationState.setCurrent(currentConversationState);
    }
    return null;
  }

  private CommentDto addComment(TaskDto taskDto, String message) {
    // Workaround for TaskService which relies on ConversationState to retrieve
    // username instead of parameter
    ConversationState currentConversationState = setCurrentConversationState();
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      String managersMention = getProjectManagersMention();
      return taskCommentService.addComment(taskDto, provisioningConfiguration.getTaskUsername(), message + managersMention);
    } catch (Exception e) {
      LOG.warn("Error creating comment on task {}", taskDto.getId(), e);
      return null;
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }

      ConversationState.setCurrent(currentConversationState);
    }
  }

  private void adTaskLabel(TaskDto task, boolean start, boolean confirmed) { // NOSONAR
    // Workaround for TaskService which relies on ConversationState to retrieve
    // username instead of parameter
    ConversationState currentConversationState = setCurrentConversationState();
    ExoContainer currentExoContainer = setCurrentContainer();
    RequestLifeCycle.begin(container);
    try {
      if (confirmed) {
        addTaskLabel(task, transactionConfirmedLabel);
        removeTaskLabel(task, transactionInProgressLabel);
      } else {
        addTaskLabel(task, transactionInProgressLabel);
      }
    } catch (Exception e) {
      LOG.warn("Error while adding Label to Task", e);
    } finally {
      RequestLifeCycle.end();
      if (currentExoContainer instanceof RootContainer) {
        ExoContainerContext.setCurrentContainer(null);
      }

      ConversationState.setCurrent(currentConversationState);
    }
  }

  private void addTaskLabel(TaskDto task, LabelDto label) {
    if (label != null) {
      // Workaround for TaskService which relies on ConversationState to
      // retrieve
      // username instead of parameter
      ConversationState currentConversationState = setCurrentConversationState();
      ExoContainer currentExoContainer = setCurrentContainer();
      RequestLifeCycle.begin(container);
      try {
        List<LabelDto> labels = taskLabelService.findLabelsByTask(task, label.getProject().getId(), userIdentity, 0, -1);
        if (labels.stream().noneMatch(labelTmp -> labelTmp.getId() == label.getId())) {
          try {
            taskLabelService.addTaskToLabel(task, label.getId());
          } catch (Exception e) {
            LOG.warn("Unable to add label {} to task {}. Ignore deleting it.", label.getName(), task.getId(), e);
          }
        }
      } finally {
        RequestLifeCycle.end();
        if (currentExoContainer instanceof RootContainer) {
          ExoContainerContext.setCurrentContainer(null);
        }
        ConversationState.setCurrent(currentConversationState);
      }
    }
  }

  private void removeTaskLabel(TaskDto task, LabelDto label) {
    if (label != null) {
      // Workaround for TaskService which relies on ConversationState to
      // retrieve
      // username instead of parameter
      ConversationState currentConversationState = setCurrentConversationState();
      ExoContainer currentExoContainer = setCurrentContainer();
      RequestLifeCycle.begin(container);
      try {
        List<LabelDto> labels = taskLabelService.findLabelsByTask(task, label.getProject().getId(), userIdentity, 0, -1);
        if (labels.stream().anyMatch(labelTmp -> labelTmp.getId() == label.getId())) {
          try {
            taskLabelService.removeTaskFromLabel(task, label.getId());
          } catch (Exception e) {
            LOG.warn("Unable to removing label {} from task {}. Ignore deleting it.", label.getName(), task.getId(), e);
          }
        }
      } finally {
        RequestLifeCycle.end();
        if (currentExoContainer instanceof RootContainer) {
          ExoContainerContext.setCurrentContainer(null);
        }
        ConversationState.setCurrent(currentConversationState);
      }
    }
  }

  private void setDeedProvisioningTaskId(DeedTenant deedTenant, String transactionHash, long taskId) {
    if (deedTenant.getProperties() == null) {
      deedTenant.setProperties(new HashMap<>());
    }
    deedTenant.getProperties().put(TASK_ID_PROP_NAME, computeTaskIdValue(taskId, transactionHash));
    deedTenant.getProperties().put(CURRENT_TASK_ID_PROP_NAME, String.valueOf(taskId));
    String oldTaskIds = deedTenant.getProperties().get(TASK_IDS_PROP_NAME);
    if (StringUtils.isBlank(oldTaskIds)) {
      deedTenant.getProperties().put(TASK_IDS_PROP_NAME, String.valueOf(taskId));
    } else {
      Set<String> taskIdsSet = new HashSet<>(Arrays.asList(oldTaskIds.split(",")));
      taskIdsSet.add(String.valueOf(taskId));
      deedTenant.getProperties().put(TASK_IDS_PROP_NAME, StringUtils.join(taskIdsSet, ","));
    }
  }

  private long getCurrentTaskId(DeedTenant deedTenant) {
    if (deedTenant != null && deedTenant.getProperties() != null
        && deedTenant.getProperties().containsKey(CURRENT_TASK_ID_PROP_NAME)) {
      String taskId = deedTenant.getProperties().get(CURRENT_TASK_ID_PROP_NAME);
      return Long.parseLong(taskId);
    }
    return 0;
  }

  private String computeTaskIdValue(long taskId, String transactionHash) {
    return transactionHash + TASK_ID_SEPARATOR + taskId;
  }

  private StatusDto getTaskStatus(boolean confirmed) {
    return confirmed ? taskConfirmedStatus : taskPendingStatus;
  }

  private TaskDto getTask(DeedTenant deedTenant, String transactionHash, boolean confirmed) {
    TaskDto taskDto = null;
    try {
      if (confirmed) {
        taskDto = getTask(deedTenant, transactionHash);
      }
    } catch (Exception e) {
      LOG.warn("Error retrieving previously created task, create new one", e);
    }
    return taskDto;
  }

  private String buildTitle(DeedTenant deedTenant, boolean start, boolean confirmed, boolean error) {
    long nftId = deedTenant.getNftId();
    String status = error ? "[Error]" : confirmed ? "[Confirmed]" : "[ComingSoon]";// NOSONAR
    String command = start ? "Deploy" : "Undeploy";
    return "[DEED] " + status + " " + command + " DEED #" + nftId + " Tenant";
  }

  private String buildDescription(DeedTenant deedTenant, String transactionHash) {
    DeedCity deedCity = DeedCity.values()[deedTenant.getCityIndex()];
    DeedCard deedCard = DeedCard.values()[deedTenant.getCardType()];

    long nftId = deedTenant.getNftId();

    StringBuilder message = new StringBuilder();
    // URL: https://<city>-<nftId>.wom.meeds.io
    message.append("<p>")
           .append("URL: ")
           .append("https://")
           .append(deedCity.name().toLowerCase())
           .append("-")
           .append(nftId)
           .append(".")
           .append(provisioningConfiguration.getUrlSuffix())
           .append("</p>");
    // Transaction hash: <Etherscan Link>
    message.append("<p>")
           .append("Transaction: ")
           .append(getEtherscanPrefix())
           .append("/tx/")
           .append(transactionHash)
           .append("</p>");
    // NFT ID: <Etherscan Link>
    message.append("<p>")
           .append("NFT ID: ")
           .append(getEtherscanPrefix())
           .append("/token/")
           .append(blockchainProperties.getDeedAddress())
           .append("?a=")
           .append(nftId)
           .append("</p>");
    // City Name
    message.append("<p>").append("City Name: ").append(deedCity.name()).append("</p>");
    // Card Type Name
    message.append("<p>").append("DEED Type: ").append(deedCard.name()).append("</p>");
    // Deed Manager Email
    if (StringUtils.isNotBlank(deedTenant.getManagerEmail())) {
      message.append("<p>").append("Contact Email: ").append(deedTenant.getManagerEmail()).append("</p>");
    }
    // Owner: <Etherscan Address>
    if (StringUtils.isNotBlank(deedTenant.getManagerAddress())) {
      message.append("<p>")
             .append("Owner: ")
             .append(getEtherscanPrefix())
             .append("/address/")
             .append(deedTenant.getManagerAddress())
             .append("</p>");
    }
    return message.toString();
  }

  private String getEtherscanPrefix() {
    if (StringUtils.isBlank(etherscanPrefix)) {
      etherscanPrefix = StringUtils.trim(provisioningConfiguration.getEtherscanPrefix());
      if (etherscanPrefix.endsWith("/")) {
        etherscanPrefix = etherscanPrefix.substring(0, etherscanPrefix.length() - 1);
      }
    }
    return etherscanPrefix;
  }

  private LabelDto getOrCreateLabel(String transactionLabel, String color) {
    String username = provisioningConfiguration.getTaskUsername();
    Long taskProjectId = provisioningConfiguration.getTaskProjectId();

    List<LabelDto> labels = taskLabelService.findLabelsByProject(taskProjectId, userIdentity, 0, -1);
    return labels.stream().filter(label -> StringUtils.equals(transactionLabel, label.getName())).findFirst().orElseGet(() -> {
      LabelDto labelDto = new LabelDto();
      labelDto.setName(transactionLabel);
      labelDto.setCanEdit(true);
      labelDto.setProject(project);
      labelDto.setUsername(username);
      labelDto.setColor(color);
      return taskLabelService.createLabel(labelDto);
    });
  }

  private String getProjectManagersMention() {
    Set<String> projectUserManagers = getProjectManagers(project.getId());
    return projectUserManagers.stream().reduce("", (result, manager) -> result += " @" + manager);
  }

  private Set<String> getProjectManagers(long projectId) {
    Set<String> managers = taskProjectService.getManager(projectId);
    if (managers != null) {
      return managers.stream().flatMap(manager -> {
        if (StringUtils.contains(manager, SPACES_PARENT_GROUP)) {
          List<String> spaceManagers = getSpaceManagers(manager);
          return Stream.of(spaceManagers.toArray(new String[0]));
        } else if (!StringUtils.contains(manager, "/")) {
          return Stream.of(manager);
        }
        return Stream.empty();
      }).collect(Collectors.toSet());
    } else {
      return Collections.emptySet();
    }
  }

  private List<String> getSpaceManagers(String spaceGroupId) {
    if (StringUtils.contains(spaceGroupId, ":")) {
      spaceGroupId = spaceGroupId.split(":")[1];
    }
    Space space = spaceService.getSpaceByGroupId(spaceGroupId);
    if (space == null) {
      return Collections.emptyList();
    } else {
      return Arrays.asList(space.getManagers());
    }
  }

  private ConversationState setCurrentConversationState() {
    // Workaround for bad Tasks API that depends on ConversationState instead of
    // API methods parameters for userId
    ConversationState currentConversationState = ConversationState.getCurrent();
    if (currentConversationState == null) {
      ConversationState.setCurrent(new ConversationState(userIdentity));
    }
    return currentConversationState;
  }

  private ExoContainer setCurrentContainer() {
    ExoContainer currentExoContainer = ExoContainerContext.getCurrentContainer();
    ExoContainerContext.setCurrentContainer(container);
    return currentExoContainer;
  }

  private void cancelPreviousTask(long previousTaskId, long newTaskId, String transactionHash) {
    try {
      TaskDto deprecatedTaskToCancel = taskService.getTask(previousTaskId);
      if (deprecatedTaskToCancel != null && !deprecatedTaskToCancel.isCompleted()) {
        LOG.info("Cancel Previous Provisioning Task #{} to replace it with with Task #{} with transaction hash {}",
                 previousTaskId,
                 newTaskId,
                 transactionHash);
        addComment(deprecatedTaskToCancel,
                   "Tenant transaction replaced by task #" + newTaskId + " with transaction " + transactionHash);

        deprecatedTaskToCancel.setCompleted(true);
        deprecatedTaskToCancel.setPriority(Priority.NONE);
        taskService.updateTask(deprecatedTaskToCancel);

        removeTaskLabel(deprecatedTaskToCancel, transactionInProgressLabel);
        addTaskLabel(deprecatedTaskToCancel, transactionConflictLabel);
      }
    } catch (Exception e) {
      LOG.warn("Error while canceling [REPLACED] Provisioning Task {} by new one {}", previousTaskId, newTaskId, e);
    }
  }

}
