package tech.easyflow.ai.easyagentsflow.config;

import com.easyagents.flow.core.chain.event.ChainStatusChangeEvent;
import com.easyagents.flow.core.chain.repository.ChainDefinitionRepository;
import com.easyagents.flow.core.chain.repository.ChainDefinitionSnapshotRepository;
import com.easyagents.flow.core.chain.repository.ChainStateRepository;
import com.easyagents.flow.core.chain.repository.LoopResultRepository;
import com.easyagents.flow.core.chain.repository.NodeStateRepository;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import com.easyagents.flow.core.chain.runtime.TriggerScheduler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tech.easyflow.ai.easyagentsflow.listener.ChainErrorListenerForSave;
import tech.easyflow.ai.easyagentsflow.listener.ChainEventListenerForSave;
import tech.easyflow.ai.easyagentsflow.listener.NodeErrorListenerForSave;
import tech.easyflow.ai.easyagentsflow.upload.WorkflowApiUploadCleanupListener;

import javax.annotation.Resource;
import java.time.Duration;

@Configuration
@EnableConfigurationProperties(WorkflowExecutionBudgetProperties.class)
public class ChainExecutorConfig {

    @Resource
    private ChainDefinitionRepository chainDefinitionRepository;
    @Resource
    private ChainStateRepository chainStateRepository;
    @Resource
    private NodeStateRepository nodeStateRepository;
    @Resource
    private LoopResultRepository loopResultRepository;
    @Resource
    private ChainDefinitionSnapshotRepository chainDefinitionSnapshotRepository;
    @Resource
    private TriggerScheduler triggerScheduler;
    @Resource
    private ChainEventListenerForSave chainEventListenerForSave;
    @Resource
    private WorkflowApiUploadCleanupListener workflowApiUploadCleanupListener;
    @Resource
    private WorkflowExecutionBudgetProperties workflowExecutionBudgetProperties;
    @Resource
    private WorkflowRuntimeProperties workflowRuntimeProperties;

    @Bean(name = "chainExecutor")
    public ChainExecutor chainExecutor() {

        ChainExecutor chainExecutor = new ChainExecutor(chainDefinitionRepository,
                chainStateRepository,
                nodeStateRepository,
                loopResultRepository,
                chainDefinitionSnapshotRepository,
                triggerScheduler,
                workflowExecutionBudgetProperties.toExecutionBudget());
        int laneMaxDepth = workflowRuntimeProperties
                .getChildWorkflowLaneMaxDepth();
        int budgetMaxDepth = workflowExecutionBudgetProperties
                .getMaxNestedDepth();
        if (laneMaxDepth <= 0
                || budgetMaxDepth <= 0
                || budgetMaxDepth > laneMaxDepth) {
            throw new IllegalStateException(
                    "easyflow.workflow.execution-budget.max-nested-depth "
                            + "must be positive and not exceed "
                            + "easyflow.workflow.runtime."
                            + "child-workflow-lane-max-depth");
        }
        Duration pollInterval = workflowRuntimeProperties
                .getChildWorkflowPollInterval();
        long pollMillis = pollInterval == null
                ? 500L
                : Math.max(100L, pollInterval.toMillis());
        chainExecutor.configureChildWorkflowRuntime(
                Math.max(1, workflowRuntimeProperties
                        .getChildWorkflowRootPermits()),
                pollMillis,
                laneMaxDepth);

        saveStepsListeners(chainExecutor);

        return chainExecutor;
    }

    /**
     * 步骤保存监听器 - 自行实现
     */
    private void saveStepsListeners(ChainExecutor chainExecutor) {
        chainExecutor.addEventListener(chainEventListenerForSave);
        chainExecutor.addEventListener(
                ChainStatusChangeEvent.class,
                workflowApiUploadCleanupListener);
        chainExecutor.addErrorListener(new ChainErrorListenerForSave());
        chainExecutor.addNodeErrorListener(new NodeErrorListenerForSave());
    }
}
