<commit_before>/*
 * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com
 *
 * 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.opensingular.server.commons.service;

import org.apache.commons.collections.CollectionUtils;
import org.opensingular.flow.core.Flow;
import org.opensingular.flow.core.MTask;
import org.opensingular.flow.core.MTransition;
import org.opensingular.flow.core.ProcessDefinition;
import org.opensingular.flow.core.ProcessInstance;
import org.opensingular.flow.core.SingularFlowException;
import org.opensingular.flow.core.TaskInstance;
import org.opensingular.flow.core.TaskType;
import org.opensingular.flow.core.variable.type.VarTypeString;
import org.opensingular.flow.persistence.entity.Actor;
import org.opensingular.flow.persistence.entity.ProcessDefinitionEntity;
import org.opensingular.flow.persistence.entity.ProcessGroupEntity;
import org.opensingular.flow.persistence.entity.ProcessInstanceEntity;
import org.opensingular.flow.persistence.entity.TaskInstanceEntity;
import org.opensingular.flow.persistence.entity.TaskVersionEntity;
import org.opensingular.form.SInstance;
import org.opensingular.form.context.SFormConfig;
import org.opensingular.form.persistence.FormKey;
import org.opensingular.form.persistence.entity.FormAnnotationEntity;
import org.opensingular.form.persistence.entity.FormEntity;
import org.opensingular.lib.commons.base.SingularException;
import org.opensingular.lib.commons.util.Loggable;
import org.opensingular.server.commons.exception.PetitionConcurrentModificationException;
import org.opensingular.server.commons.exception.SingularServerException;
import org.opensingular.server.commons.flow.rest.ActionConfig;
import org.opensingular.server.commons.form.FormActions;
import org.opensingular.server.commons.persistence.dao.flow.ActorDAO;
import org.opensingular.server.commons.persistence.dao.flow.GrupoProcessoDAO;
import org.opensingular.server.commons.persistence.dao.flow.TaskInstanceDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionContentHistoryDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionerDAO;
import org.opensingular.server.commons.persistence.dto.PeticaoDTO;
import org.opensingular.server.commons.persistence.dto.PetitionHistoryDTO;
import org.opensingular.server.commons.persistence.dto.TaskInstanceDTO;
import org.opensingular.server.commons.persistence.entity.form.FormPetitionEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionContentHistoryEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionerEntity;
import org.opensingular.server.commons.persistence.filter.QuickFilter;
import org.opensingular.server.commons.service.dto.BoxItemAction;
import org.opensingular.server.commons.spring.security.AuthorizationService;
import org.opensingular.server.commons.spring.security.SingularPermission;
import org.opensingular.server.commons.util.PetitionUtil;
import org.opensingular.server.commons.wicket.view.form.FormPageConfig;
import org.opensingular.server.commons.wicket.view.util.DispatcherPageUtil;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.opensingular.lib.support.persistence.enums.SimNao.SIM;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_ANALYSE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_ASSIGN;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_DELETE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_EDIT;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_RELOCATE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_VIEW;
import static org.opensingular.server.commons.flow.rest.DefaultServerREST.DELETE;
import static org.opensingular.server.commons.flow.rest.DefaultServerREST.PATH_BOX_ACTION;
import static org.opensingular.server.commons.util.DispatcherPageParameters.FORM_NAME;

@Transactional
public class PetitionService<P extends PetitionEntity> implements Loggable {

    @Inject
    protected PetitionDAO<P> petitionDAO;

    @Inject
    protected GrupoProcessoDAO grupoProcessoDAO;

    @Inject
    protected TaskInstanceDAO taskInstanceDAO;

    @Inject
    protected PetitionerDAO petitionerDAO;

    @Inject
    protected PetitionContentHistoryDAO petitionContentHistoryDAO;

    @Inject
    protected AuthorizationService authorizationService;

    @Inject
    protected FormPetitionService<P> formPetitionService;

    @Inject
    protected ActorDAO actorDAO;

    public P findPetitionByCod(Long cod) {
        return petitionDAO.find(cod);
    }

    public P findPetitionByProcessCod(Integer cod) {
        return petitionDAO.findByProcessCod(cod);
    }

    public void deletePetition(PeticaoDTO peticao) {
        petitionDAO.delete(petitionDAO.find(peticao.getCodPeticao()));
    }

    public void deletePetition(Long idPeticao) {
        petitionDAO.delete(petitionDAO.find(idPeticao));
    }

    public Long countQuickSearch(QuickFilter filter) {
        return countQuickSearch(filter, filter.getProcessesAbbreviation(), filter.getTypesNames());
    }

    public Long countQuickSearch(QuickFilter filter, List<String> siglasProcesso, List<String> formNames) {
        return petitionDAO.countQuickSearch(filter, siglasProcesso, formNames);
    }

    public List<PeticaoDTO> quickSearch(QuickFilter filter, List<String> siglasProcesso, List<String> formNames) {
        return petitionDAO.quickSearch(filter, siglasProcesso, formNames);
    }

    public List<Map<String, Object>> quickSearchMap(QuickFilter filter) {
        final List<Map<String, Object>> list = petitionDAO.quickSearchMap(filter, filter.getProcessesAbbreviation(), filter.getTypesNames());
        parseResultsPetition(list);
        list.forEach(this::checkItemActions);
        for (Map<String, Object> map : list) {
            authorizationService.filterActions((String) map.get("type"), (Long) map.get("codPeticao"), (List<BoxItemAction>) map.get("actions"), filter.getIdUsuarioLogado());
        }
        return list;
    }

    protected void parseResultsPetition(List<Map<String, Object>> results) {

    }

    private void checkItemActions(Map<String, Object> item) {
        List<BoxItemAction> actions = new ArrayList<>();
        actions.add(createPopupBoxItemAction(item, FormActions.FORM_FILL, ACTION_EDIT.getName()));
        actions.add(createPopupBoxItemAction(item, FormActions.FORM_VIEW, ACTION_VIEW.getName()));
        actions.add(createDeleteAction(item));
        actions.add(BoxItemAction.newExecuteInstante(item.get("codPeticao"), ACTION_ASSIGN.getName()));

        appendItemActions(item, actions);

        String processKey = (String) item.get("processType");


        ActionConfig tryConfig = null;
        try {
            final ProcessDefinition<?> processDefinition = Flow.getProcessDefinitionWith(processKey);
            tryConfig = processDefinition.getMetaDataValue(ActionConfig.KEY);
        } catch (SingularException e) {

            getLogger().error(e.getMessage());
        }

        final ActionConfig actionConfig = tryConfig;
        if (actionConfig != null) {
            actions = actions.stream()
                    .filter(itemAction -> actionConfig.containsAction(itemAction.getName()))
                    .collect(Collectors.toList());
        }

        item.put("actions", actions);
    }

    protected void appendItemActions(Map<String, Object> item, List<BoxItemAction> actions) {
    }

    private BoxItemAction createDeleteAction(Map<String, Object> item) {
        String endpointUrl = PATH_BOX_ACTION + DELETE + "?id=" + item.get("codPeticao");

        final BoxItemAction boxItemAction = new BoxItemAction();
        boxItemAction.setName(ACTION_DELETE.getName());
        boxItemAction.setEndpoint(endpointUrl);
        return boxItemAction;
    }

    protected BoxItemAction createPopupBoxItemAction(Map<String, Object> item, FormActions formAction, String actionName) {
        Object cod  = item.get("codPeticao");
        Object type = item.get("type");
        return createPopupBoxItemAction(cod, type, formAction, actionName);
    }

    private BoxItemAction createPopupBoxItemAction(Object cod, Object type, FormActions formAction, String actionName) {
        String endpoint = DispatcherPageUtil
                .baseURL("")
                .formAction(formAction.getId())
                .petitionId(cod)
                .param(FORM_NAME, type)
                .build();

        final BoxItemAction boxItemAction = new BoxItemAction();
        boxItemAction.setName(actionName);
        boxItemAction.setEndpoint(endpoint);
        boxItemAction.setFormAction(formAction);
        return boxItemAction;
    }

    public FormKey saveOrUpdate(P peticao, SInstance instance, boolean mainForm, SFormConfig config) {
        return saveOrUpdate(peticao, instance, mainForm, config, null);
    }

    public FormKey saveOrUpdate(P petition,
                                SInstance instance,
                                boolean mainForm,
                                SFormConfig config,
                                Consumer<P> onSave) {

        if (instance == null) {
            return null;
        }

        petitionDAO.saveOrUpdate(petition);

        if (petition.getPetitioner() != null) {
            petitionerDAO.saveOrUpdate(petition.getPetitioner());
        }

        final FormKey key = formPetitionService.saveFormPetition(petition, instance, mainForm, config);

        if (onSave != null) {
            onSave.accept(petition);
        }

        return key;
    }

    public void send(P peticao, SInstance instance, String codResponsavel, SFormConfig config) {

        formPetitionService.consolidateDrafts(peticao, config);

        final ProcessDefinition<?>  processDefinition = PetitionUtil.getProcessDefinition(peticao);
        final ProcessInstance       processInstance   = processDefinition.newInstance();
        final ProcessInstanceEntity processEntity     = processInstance.saveEntity();

        processInstance.setDescription(peticao.getDescription());
        peticao.setProcessInstanceEntity(processEntity);

        processInstance.start();

        onSend(peticao, instance, processEntity, codResponsavel);

        savePetitionHistory(peticao);
    }

    protected void onSend(P peticao, SInstance instance, ProcessInstanceEntity processEntity, String codResponsavel) {
    }

    private void savePetitionHistory(PetitionEntity petition) {

        final TaskInstanceEntity taskInstance = findCurrentTaskByPetitionId(petition.getCod());
        final FormEntity         formEntity   = petition.getMainForm();

        getLogger().info("Atualizando histórico da petição.");

        final PetitionContentHistoryEntity contentHistoryEntity = new PetitionContentHistoryEntity();

        contentHistoryEntity.setPetitionEntity(petition);

        if (taskInstance != null) {
            contentHistoryEntity.setActor(taskInstance.getAllocatedUser());
            contentHistoryEntity.setTaskInstanceEntity(taskInstance);
        }

        if (CollectionUtils.isNotEmpty(formEntity.getCurrentFormVersionEntity().getFormAnnotations())) {
            contentHistoryEntity.setFormAnnotationsVersions(formEntity.getCurrentFormVersionEntity().getFormAnnotations().stream().map(FormAnnotationEntity::getAnnotationCurrentVersion).collect(Collectors.toList()));
        }

        contentHistoryEntity.setPetitionerEntity(petition.getPetitioner());
        contentHistoryEntity.setHistoryDate(new Date());

        petitionContentHistoryDAO.saveOrUpdate(contentHistoryEntity);

        contentHistoryEntity.setFormVersionHistoryEntities(
                petition
                        .getFormPetitionEntities()
                        .stream()
                        .filter(isMainFormOrIsForCurrentTaskDefinition(petition))
                        .map(f -> formPetitionService.createFormVersionHistory(contentHistoryEntity, f))
                        .collect(Collectors.toList())
        );
    }

    private Predicate<FormPetitionEntity> isMainFormOrIsForCurrentTaskDefinition(PetitionEntity petitionEntity) {
        return f ->
                Optional.ofNullable(f)
                        .map(FormPetitionEntity::getMainForm)
                        .map(SIM::equals)
                        .orElse(false)
                        ||
                        Optional.ofNullable(petitionEntity)
                                .map(PetitionEntity::getProcessInstanceEntity)
                                .map(ProcessInstanceEntity::getCurrentTask)
                                .map(TaskInstanceEntity::getTask)
                                .map(TaskVersionEntity::getTaskDefinition)
                                .map(definition -> Optional.ofNullable(f)
                                        .map(FormPetitionEntity::getTaskDefinitionEntity)
                                        .map(definition::equals)
                                        .orElse(false)
                                )
                                .orElse(false);
    }

    /**
     * Executa a transição informada, consolidando todos os rascunhos, este metodo não salva a petição
     *
     * @param tn           nome tra transicao
     * @param petition     peticao
     * @param cfg          formConfig
     * @param onTransition listener
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void executeTransition(String tn, P petition, SFormConfig cfg, BiConsumer<P, String> onTransition, Map<String, String> params) {
        try {
            if (onTransition != null) {
                onTransition.accept(petition, tn);<commit_msg>[GGTOX] - Implementando atirbuição automática de tarefa durante a devolução/cumprimento de exigência.<commit_after>/*
 * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com
 *
 * 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.opensingular.server.commons.service;

import org.apache.commons.collections.CollectionUtils;
import org.opensingular.flow.core.Flow;
import org.opensingular.flow.core.MTask;
import org.opensingular.flow.core.MTransition;
import org.opensingular.flow.core.ProcessDefinition;
import org.opensingular.flow.core.ProcessInstance;
import org.opensingular.flow.core.SingularFlowException;
import org.opensingular.flow.core.TaskInstance;
import org.opensingular.flow.core.TaskType;
import org.opensingular.flow.core.variable.type.VarTypeString;
import org.opensingular.flow.persistence.entity.Actor;
import org.opensingular.flow.persistence.entity.ProcessDefinitionEntity;
import org.opensingular.flow.persistence.entity.ProcessGroupEntity;
import org.opensingular.flow.persistence.entity.ProcessInstanceEntity;
import org.opensingular.flow.persistence.entity.TaskInstanceEntity;
import org.opensingular.flow.persistence.entity.TaskVersionEntity;
import org.opensingular.form.SInstance;
import org.opensingular.form.context.SFormConfig;
import org.opensingular.form.persistence.FormKey;
import org.opensingular.form.persistence.entity.FormAnnotationEntity;
import org.opensingular.form.persistence.entity.FormEntity;
import org.opensingular.lib.commons.base.SingularException;
import org.opensingular.lib.commons.util.Loggable;
import org.opensingular.server.commons.exception.PetitionConcurrentModificationException;
import org.opensingular.server.commons.exception.SingularServerException;
import org.opensingular.server.commons.flow.rest.ActionConfig;
import org.opensingular.server.commons.form.FormActions;
import org.opensingular.server.commons.persistence.dao.flow.ActorDAO;
import org.opensingular.server.commons.persistence.dao.flow.GrupoProcessoDAO;
import org.opensingular.server.commons.persistence.dao.flow.TaskInstanceDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionContentHistoryDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionDAO;
import org.opensingular.server.commons.persistence.dao.form.PetitionerDAO;
import org.opensingular.server.commons.persistence.dto.PeticaoDTO;
import org.opensingular.server.commons.persistence.dto.PetitionHistoryDTO;
import org.opensingular.server.commons.persistence.dto.TaskInstanceDTO;
import org.opensingular.server.commons.persistence.entity.form.FormPetitionEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionContentHistoryEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionEntity;
import org.opensingular.server.commons.persistence.entity.form.PetitionerEntity;
import org.opensingular.server.commons.persistence.filter.QuickFilter;
import org.opensingular.server.commons.service.dto.BoxItemAction;
import org.opensingular.server.commons.spring.security.AuthorizationService;
import org.opensingular.server.commons.spring.security.SingularPermission;
import org.opensingular.server.commons.util.PetitionUtil;
import org.opensingular.server.commons.wicket.view.form.FormPageConfig;
import org.opensingular.server.commons.wicket.view.util.DispatcherPageUtil;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.opensingular.lib.support.persistence.enums.SimNao.SIM;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_ANALYSE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_ASSIGN;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_DELETE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_EDIT;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_RELOCATE;
import static org.opensingular.server.commons.flow.action.DefaultActions.ACTION_VIEW;
import static org.opensingular.server.commons.flow.rest.DefaultServerREST.DELETE;
import static org.opensingular.server.commons.flow.rest.DefaultServerREST.PATH_BOX_ACTION;
import static org.opensingular.server.commons.util.DispatcherPageParameters.FORM_NAME;

@Transactional
public class PetitionService<P extends PetitionEntity> implements Loggable {

    @Inject
    protected PetitionDAO<P> petitionDAO;

    @Inject
    protected GrupoProcessoDAO grupoProcessoDAO;

    @Inject
    protected TaskInstanceDAO taskInstanceDAO;

    @Inject
    protected PetitionerDAO petitionerDAO;

    @Inject
    protected PetitionContentHistoryDAO petitionContentHistoryDAO;

    @Inject
    protected AuthorizationService authorizationService;

    @Inject
    protected FormPetitionService<P> formPetitionService;

    @Inject
    protected ActorDAO actorDAO;

    public P findPetitionByCod(Long cod) {
        return petitionDAO.find(cod);
    }

    public P findPetitionByProcessCod(Integer cod) {
        return petitionDAO.findByProcessCod(cod);
    }

    public void deletePetition(PeticaoDTO peticao) {
        petitionDAO.delete(petitionDAO.find(peticao.getCodPeticao()));
    }

    public void deletePetition(Long idPeticao) {
        petitionDAO.delete(petitionDAO.find(idPeticao));
    }

    public Long countQuickSearch(QuickFilter filter) {
        return countQuickSearch(filter, filter.getProcessesAbbreviation(), filter.getTypesNames());
    }

    public Long countQuickSearch(QuickFilter filter, List<String> siglasProcesso, List<String> formNames) {
        return petitionDAO.countQuickSearch(filter, siglasProcesso, formNames);
    }

    public List<PeticaoDTO> quickSearch(QuickFilter filter, List<String> siglasProcesso, List<String> formNames) {
        return petitionDAO.quickSearch(filter, siglasProcesso, formNames);
    }

    public List<Map<String, Object>> quickSearchMap(QuickFilter filter) {
        final List<Map<String, Object>> list = petitionDAO.quickSearchMap(filter, filter.getProcessesAbbreviation(), filter.getTypesNames());
        parseResultsPetition(list);
        list.forEach(this::checkItemActions);
        for (Map<String, Object> map : list) {
            authorizationService.filterActions((String) map.get("type"), (Long) map.get("codPeticao"), (List<BoxItemAction>) map.get("actions"), filter.getIdUsuarioLogado());
        }
        return list;
    }

    protected void parseResultsPetition(List<Map<String, Object>> results) {

    }

    private void checkItemActions(Map<String, Object> item) {
        List<BoxItemAction> actions = new ArrayList<>();
        actions.add(createPopupBoxItemAction(item, FormActions.FORM_FILL, ACTION_EDIT.getName()));
        actions.add(createPopupBoxItemAction(item, FormActions.FORM_VIEW, ACTION_VIEW.getName()));
        actions.add(createDeleteAction(item));
        actions.add(BoxItemAction.newExecuteInstante(item.get("codPeticao"), ACTION_ASSIGN.getName()));

        appendItemActions(item, actions);

        String processKey = (String) item.get("processType");


        ActionConfig tryConfig = null;
        try {
            final ProcessDefinition<?> processDefinition = Flow.getProcessDefinitionWith(processKey);
            tryConfig = processDefinition.getMetaDataValue(ActionConfig.KEY);
        } catch (SingularException e) {

            getLogger().error(e.getMessage());
        }

        final ActionConfig actionConfig = tryConfig;
        if (actionConfig != null) {
            actions = actions.stream()
                    .filter(itemAction -> actionConfig.containsAction(itemAction.getName()))
                    .collect(Collectors.toList());
        }

        item.put("actions", actions);
    }

    protected void appendItemActions(Map<String, Object> item, List<BoxItemAction> actions) {
    }

    private BoxItemAction createDeleteAction(Map<String, Object> item) {
        String endpointUrl = PATH_BOX_ACTION + DELETE + "?id=" + item.get("codPeticao");

        final BoxItemAction boxItemAction = new BoxItemAction();
        boxItemAction.setName(ACTION_DELETE.getName());
        boxItemAction.setEndpoint(endpointUrl);
        return boxItemAction;
    }

    protected BoxItemAction createPopupBoxItemAction(Map<String, Object> item, FormActions formAction, String actionName) {
        Object cod  = item.get("codPeticao");
        Object type = item.get("type");
        return createPopupBoxItemAction(cod, type, formAction, actionName);
    }

    private BoxItemAction createPopupBoxItemAction(Object cod, Object type, FormActions formAction, String actionName) {
        String endpoint = DispatcherPageUtil
                .baseURL("")
                .formAction(formAction.getId())
                .petitionId(cod)
                .param(FORM_NAME, type)
                .build();

        final BoxItemAction boxItemAction = new BoxItemAction();
        boxItemAction.setName(actionName);
        boxItemAction.setEndpoint(endpoint);
        boxItemAction.setFormAction(formAction);
        return boxItemAction;
    }

    public FormKey saveOrUpdate(P peticao, SInstance instance, boolean mainForm, SFormConfig config) {
        return saveOrUpdate(peticao, instance, mainForm, config, null);
    }

    public FormKey saveOrUpdate(P petition,
                                SInstance instance,
                                boolean mainForm,
                                SFormConfig config,
                                Consumer<P> onSave) {

        if (instance == null) {
            return null;
        }

        petitionDAO.saveOrUpdate(petition);

        if (petition.getPetitioner() != null) {
            petitionerDAO.saveOrUpdate(petition.getPetitioner());
        }

        final FormKey key = formPetitionService.saveFormPetition(petition, instance, mainForm, config);

        if (onSave != null) {
            onSave.accept(petition);
        }

        return key;
    }

    public void send(P peticao, SInstance instance, String codResponsavel, SFormConfig config) {

        formPetitionService.consolidateDrafts(peticao, config);

        final ProcessDefinition<?>  processDefinition = PetitionUtil.getProcessDefinition(peticao);
        final ProcessInstance       processInstance   = processDefinition.newInstance();
        final ProcessInstanceEntity processEntity     = processInstance.saveEntity();

        processInstance.setDescription(peticao.getDescription());
        peticao.setProcessInstanceEntity(processEntity);

        processInstance.start();

        onSend(peticao, instance, processEntity, codResponsavel);

        savePetitionHistory(peticao);
    }

    protected void onSend(P peticao, SInstance instance, ProcessInstanceEntity processEntity, String codResponsavel) {
    }

    private void savePetitionHistory(PetitionEntity petition) {

        final TaskInstanceEntity taskInstance = findCurrentTaskByPetitionId(petition.getCod());
        final FormEntity         formEntity   = petition.getMainForm();

        getLogger().info("Atualizando histórico da petição.");

        final PetitionContentHistoryEntity contentHistoryEntity = new PetitionContentHistoryEntity();

        contentHistoryEntity.setPetitionEntity(petition);

        if (taskInstance != null) {
            contentHistoryEntity.setActor(taskInstance.getAllocatedUser());
            contentHistoryEntity.setTaskInstanceEntity(taskInstance);
        }

        if (CollectionUtils.isNotEmpty(formEntity.getCurrentFormVersionEntity().getFormAnnotations())) {
            contentHistoryEntity.setFormAnnotationsVersions(formEntity.getCurrentFormVersionEntity().getFormAnnotations().stream().map(FormAnnotationEntity::getAnnotationCurrentVersion).collect(Collectors.toList()));
        }

        contentHistoryEntity.setPetitionerEntity(petition.getPetitioner());
        contentHistoryEntity.setHistoryDate(new Date());

        petitionContentHistoryDAO.saveOrUpdate(contentHistoryEntity);

        contentHistoryEntity.setFormVersionHistoryEntities(
                petition
                        .getFormPetitionEntities()
                        .stream()
                        .filter(isMainFormOrIsForCurrentTaskDefinition(petition))
                        .map(f -> formPetitionService.createFormVersionHistory(contentHistoryEntity, f))
                        .collect(Collectors.toList())
        );
    }

    private Predicate<FormPetitionEntity> isMainFormOrIsForCurrentTaskDefinition(PetitionEntity petitionEntity) {
        return f ->
                Optional.ofNullable(f)
                        .map(FormPetitionEntity::getMainForm)
                        .map(SIM::equals)
                        .orElse(false)
                        ||
                        Optional.ofNullable(petitionEntity)
                                .map(PetitionEntity::getProcessInstanceEntity)
                                .map(ProcessInstanceEntity::getCurrentTask)
                                .map(TaskInstanceEntity::getTask)
                                .map(TaskVersionEntity::getTaskDefinition)
                                .map(definition -> Optional.ofNullable(f)
                                        .map(FormPetitionEntity::getTaskDefinitionEntity)
                                        .map(definition::equals)
                                        .orElse(false)
                                )
                                .orElse(false);
    }

    /**
     * Executa a transição informada, consolidando todos os rascunhos, este metodo não salva a petição
     *
     * @param tn           nome da transicao
     * @param petition     peticao
     * @param cfg          formConfig
     * @param onTransition listener
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void executeTransition(String tn, P petition, SFormConfig cfg, BiConsumer<P, String> onTransition, Map<String, String> params) {
        try {
            if (onTransition != null) {
                onTransition.accept(petition, tn);
{'fraction_non_alphanumeric': 0.08994460298808125, 'fraction_numerical': 0.0005371831458787981, 'mean_word_length': 3.421255751818317, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7065013', 'n_tokens_mistral': 8005, 'n_tokens_neox': 7784, 'n_words': 1409}