content: /*
   * Copyright 2017-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.activiti.cloud.acc.core.steps.runtime;
  
  import static org.activiti.cloud.acc.core.assertions.RestErrorAssert.assertThatRestNotFoundErrorIsThrownBy;
  import static org.activiti.cloud.services.common.util.ImageUtils.svgToPng;
  import static org.assertj.core.api.Assertions.assertThat;
  
  import com.fasterxml.jackson.databind.ObjectMapper;
  import java.io.IOException;
  import java.math.BigDecimal;
  import java.text.SimpleDateFormat;
  import java.time.Instant;
  import java.util.Collection;
  import java.util.Date;
  import java.util.List;
  import java.util.Map;
  import java.util.stream.Collectors;
  import net.thucydides.core.annotations.Step;
  import org.activiti.api.process.model.ProcessDefinition;
  import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
  import org.activiti.api.process.model.builders.StartProcessPayloadBuilder;
  import org.activiti.api.process.model.payloads.ReceiveMessagePayload;
  import org.activiti.api.process.model.payloads.StartMessagePayload;
  import org.activiti.api.process.model.payloads.StartProcessPayload;
  import org.activiti.api.runtime.model.impl.ProcessVariableValue;
  import org.activiti.cloud.acc.core.rest.PageRequest;
  import org.activiti.cloud.acc.core.rest.RuntimeDirtyContextHandler;
  import org.activiti.cloud.acc.core.rest.feign.EnableRuntimeFeignContext;
  import org.activiti.cloud.acc.core.services.runtime.diagram.ProcessRuntimeDiagramService;
  import org.activiti.cloud.acc.shared.service.BaseService;
  import org.activiti.cloud.api.process.model.CloudProcessInstance;
  import org.activiti.cloud.api.task.model.CloudTask;
  import org.activiti.cloud.services.rest.api.ProcessDefinitionsApiClient;
  import org.activiti.cloud.services.rest.api.ProcessInstanceApiClient;
  import org.activiti.cloud.services.rest.api.ProcessInstanceTasksApiClient;
  import org.apache.commons.lang3.StringUtils;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Qualifier;
  import org.springframework.data.domain.Pageable;
  import org.springframework.hateoas.EntityModel;
  
  @EnableRuntimeFeignContext
  public class ProcessRuntimeBundleSteps {
  
      private static final Pageable DEFAULT_PAGEABLE = PageRequest.of(0, 100);
  
      @Autowired
      private RuntimeDirtyContextHandler dirtyContextHandler;
  
      @Autowired
      private ProcessInstanceApiClient processInstanceApiClient;
  
      @Autowired
      private ProcessInstanceTasksApiClient processInstanceTasksApiClient;
  
      @Autowired
      private ProcessDefinitionsApiClient processDefinitionsApiClient;
  
      @Autowired
      private ProcessRuntimeDiagramService processRuntimeDiagramService;
  
      @Autowired
      private ObjectMapper objectMapper;
  
      @Autowired
      @Qualifier("runtimeBundleBaseService")
      private BaseService baseService;
  
      @Step
      public void checkServicesHealth() {
          assertThat(baseService.isServiceUp()).isTrue();
      }
  
      @Step
      public CloudProcessInstance startProcess(StartProcessPayload payload) {
          return dirtyContextHandler.dirty(processInstanceApiClient.startProcess(payload).getContent());
      }
  
      @Step
      public CloudProcessInstance startProcess(String process, boolean variables, String businessKey) throws IOException {
          StartProcessPayloadBuilder payload = ProcessPayloadBuilder
              .start()
              .withProcessDefinitionKey(process)
              .withName("processInstanceName")
              .withBusinessKey(businessKey);
  
          if (variables) {
              payload.withVariable("test_variable_name", "test-variable-value");
              payload.withVariable("test_bigdecimal_variable_name", wrap(BigDecimal.valueOf(1234567890L, 2)));
              payload.withVariable("test_date_variable_name", wrap(Date.from(Instant.EPOCH)));
              payload.withVariable("test_long_variable_name", wrap(1234567890L));
              payload.withVariable("test_int_variable_name", 7);
              payload.withVariable("test_bool_variable_name", true);
              payload.withVariable(
                  "test_json_variable_name",
                  objectMapper.readTree("{ \"test-json-variable-element1\":\"test-json-variable-value1\"}")
              );
              payload.withVariable(
                  "test_long_json_variable_name",
                  objectMapper.readTree("{ \"verylongjson\":\"" + StringUtils.repeat("a", 4000) + "\"}")
              );
          }
  
          return startProcess(payload.build());
      }
  
      protected Map<String, String> wrap(BigDecimal value) {
          return ProcessVariableValue.builder().type("bigdecimal").value(value.toString()).build().toMap();
      }
  
      protected Map<String, String> wrap(Long value) {
          return ProcessVariableValue.builder().type("long").value(value.toString()).build().toMap();
      }
  
      protected Map<String, String> wrap(Date value) {
          SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
  
          return ProcessVariableValue.builder().type("date").value(formatter.format(value)).build().toMap();
      }
  
      @Step
      public CloudProcessInstance startProcess(String process, boolean variables) throws IOException {
          return startProcess(process, variables, "businessKey");
      }
  
      @Step
      public CloudProcessInstance startProcess(String process) {
          return startProcess(
              ProcessPayloadBuilder.start().withProcessDefinitionKey(process).withName("process-instance-name").build()
          );
      }
  
      @Step
      public CloudProcessInstance startProcessWithVariables(String process, Map<String, Object> variables) {
          return startProcess(
              ProcessPayloadBuilder.start().withProcessDefinitionKey(process).withVariables(variables).build()
          );
      }
  
      @Step
      public void deleteProcessInstance(String id) {
          processInstanceApiClient.deleteProcessInstance(id);
      }
  
      @Step
      public void checkProcessInstanceNotFound(String processInstanceId) {
          assertThatRestNotFoundErrorIsThrownBy(() ->
                  processInstanceApiClient.getProcessInstanceById(processInstanceId).getContent()
              )
              .withMessageContaining("Unable to find process instance for the given id:'" + processInstanceId + "'");
      }
  
      @Step
      public String openProcessInstanceDiagram(String id) {
          return processRuntimeDiagramService.getProcessInstanceModel(id);
      }
  
      @Step
      public void checkProcessInstanceDiagram(String diagram) throws Exception {
          assertThat(diagram).isNotEmpty();
          assertThat(svgToPng(diagram.getBytes())).isNotEmpty();
      }
  
      @Step
      public void checkProcessInstanceNoDiagram(String diagram) {
          assertThat(diagram).isNullOrEmpty();
      }
  
      @Step
      public void suspendProcessInstance(String processInstanceId) {
          processInstanceApiClient.suspend(processInstanceId);
      }
  
      @Step
      public void resumeProcessInstance(String processInstanceId) {
          processInstanceApiClient.resume(processInstanceId);
      }
  
      @Step
      public Collection<CloudProcessInstance> getAllProcessInstances() {
          return processInstanceApiClient
              .getProcessInstances(DEFAULT_PAGEABLE)
              .getContent()
              .stream()
              .map(EntityModel::getContent)
              .collect(Collectors.toList());
      }
  
      @Step
      public CloudProcessInstance getProcessInstanceById(String id) {
          return processInstanceApiClient.getProcessInstanceById(id).getContent();
      }
  
      @Step
      public Collection<CloudProcessInstance> getSubProcesses(String parentId) {
          return processInstanceApiClient
              .subprocesses(parentId, DEFAULT_PAGEABLE)
              .getContent()
              .stream()
              .map(EntityModel::getContent)
              .collect(Collectors.toList());
      }
  
      @Step
      public Collection<ProcessDefinition> getProcessDefinitions() {
          return processDefinitionsApiClient
              .getProcessDefinitions(List.of(), null, DEFAULT_PAGEABLE)
              .getContent()
              .stream()
              .map(EntityModel::getContent)
              .collect(Collectors.toList());
      }
  
      @Step
      public ProcessDefinition getProcessDefinitionByKey(String key) {
          return processDefinitionsApiClient.getProcessDefinition(key).getContent();
      }
  
      @Step
      public String getProcessDiagramByKey(String key) {
          return processRuntimeDiagramService.getProcessDefinitionModel(key);
      }
  
      @Step
      public Collection<CloudTask> getTaskByProcessInstanceId(String processInstanceId) {
          return processInstanceTasksApiClient
              .getTasks(processInstanceId, DEFAULT_PAGEABLE)
              .getContent()
              .stream()
              .map(EntityModel::getContent)
              .collect(Collectors.toList());
      }
  
      @Step
      public CloudProcessInstance startProcessWithProcessInstanceName(String process, String processName) {
          return dirtyContextHandler.dirty(
              processInstanceApiClient
                  .startProcess(
                      ProcessPayloadBuilder.start().withName(processName).withProcessDefinitionKey(process).build()
                  )
                  .getContent()
          );
      }
  
      @Step
      public void checkProcessInstanceName(String processInstanceId, String processInstanceName) {
          assertThat(processInstanceApiClient.getProcessInstanceById(processInstanceId).getContent().getName())
              .isEqualTo(processInstanceName);
      }
  
      @Step
      public CloudProcessInstance setProcessName(String processInstanceId, String processInstanceName) {
          return processInstanceApiClient
              .updateProcess(processInstanceId, ProcessPayloadBuilder.update().withName(processInstanceName).build())
              .getContent();
      }
  
      @Step
      public CloudProcessInstance message(StartMessagePayload payload) throws IOException {
          return dirtyContextHandler.dirty(processInstanceApiClient.sendStartMessage(payload).getContent());
      }
  
      @Step
      public void message(ReceiveMessagePayload payload) throws IOException {
          processInstanceApiClient.receive(payload);
      }
  }
  