package com.flowable.local.work.events;

import com.flowable.audit.api.AuditService;
import com.flowable.dataobject.api.event.FlowableDataObjectEventType;
import com.flowable.dataobject.api.runtime.DataObjectInstanceVariableContainer;
import com.flowable.dataobject.api.runtime.DataObjectRuntimeService;
import com.flowable.dataobject.engine.delegate.event.impl.FlowableDataObjectInstanceDeletedEventImpl;
import com.flowable.dataobject.engine.delegate.event.impl.FlowableDataObjectInstanceUpdatedEventImpl;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.api.delegate.event.FlowableEventType;
import org.flowable.common.engine.impl.cfg.TransactionState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;


@Component
public class CustomDataObjectEventHandler implements FlowableEventListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(CustomDataObjectEventHandler.class);
    protected final AuditService auditService;
    protected final DataObjectRuntimeService dataObjectRuntimeService;

    public CustomDataObjectEventHandler(AuditService auditService, DataObjectRuntimeService dataObjectRuntimeService) {
        this.auditService = auditService;
        this.dataObjectRuntimeService = dataObjectRuntimeService;
    }

    @Override
    public void onEvent(FlowableEvent flowableEvent) {
        DataObjectInstanceVariableContainer dataObjectInstance;
        String subType;
        if (flowableEvent instanceof FlowableDataObjectInstanceUpdatedEventImpl) {
            dataObjectInstance = ((FlowableDataObjectInstanceUpdatedEventImpl) flowableEvent).getDataObjectInstance();
            subType = "updated";
        } else if (flowableEvent instanceof FlowableDataObjectInstanceDeletedEventImpl) {
            dataObjectInstance = ((FlowableDataObjectInstanceDeletedEventImpl) flowableEvent).getDataObjectInstance();
            subType = "deleted";
        } else {
            LOGGER.warn("Unhandled FlowableDataObjectEvent type: {}", flowableEvent.getClass().getName());
            return;
        }

        HashMap<String, Object> data = (HashMap<String, Object>) dataObjectInstance.getData();
        if (data == null) {
            LOGGER.warn("FlowableDataObjectEvent: No data");
            return;
        }

        // If an instance of TST_DO001 is deactivated, delete all references in other data objects.
        String scopeDefinitionId = dataObjectInstance.getDefinitionKey();
        if (scopeDefinitionId == null || !scopeDefinitionId.equals("TST_DO001")) return;
        Object active = data.get("active");
        if (!(active instanceof Boolean)) {
            LOGGER.warn("FlowableDataObjectEvent: 'active' is not boolean");
            return;
        }
        if (subType.equals("updated") && !((Boolean) active) || subType.equals("deleted")) {
            String key = (String) data.get("key");
            LOGGER.warn("delete all references of {}", key);
            dataObjectRuntimeService.createDataObjectModificationBuilder()
                    .tenantId(dataObjectInstance.getTenantId())
                    .definitionKey("TST_DO002")
                    .operation("updateTeam")
                    .value("teamKey", key)
                    .value("team", null)
                    .modify();
        }
    }

    @Override
    public boolean isFailOnException() {
        return true;
    }

    @Override
    public boolean isFireOnTransactionLifecycleEvent() {
        return false;
    }

    @Override
    public String getOnTransaction() {
        return TransactionState.COMMITTING.name();
    }

    @Override
    public Collection<? extends FlowableEventType> getTypes() {
        List<FlowableEventType> eventTypes = new ArrayList<>();
        eventTypes.add(FlowableDataObjectEventType.DATA_OBJECT_INSTANCE_UPDATED);
        eventTypes.add(FlowableDataObjectEventType.DATA_OBJECT_INSTANCE_DELETED);
        return eventTypes;
    }
}
