package tech.easyflow.ai.node;

import com.easyagents.core.model.chat.tool.Tool;
import com.alibaba.fastjson.JSON;
import com.easyagents.flow.core.chain.Chain;
import com.easyagents.flow.core.node.BaseNode;
import tech.easyflow.ai.entity.Plugin;
import tech.easyflow.ai.entity.PluginItem;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PluginType;
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityDecision;
import tech.easyflow.ai.plugin.workflow.availability.WorkflowPluginAvailabilityService;
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
import tech.easyflow.ai.service.PluginService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.utils.WorkFlowUtil;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.util.SpringContextUtil;

import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.util.Map;

public class PluginToolNode extends BaseNode {

    private BigInteger pluginId;

    public PluginToolNode() {
    }

    public PluginToolNode(BigInteger pluginId) {
        this.pluginId = pluginId;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> execute(Chain chain) {
        Map<String, Object> map = chain.getState().resolveParameters(this);
        PluginItemService bean = SpringContextUtil.getBean(PluginItemService.class);
        PluginItem tool = bean.getById(pluginId);
        if (tool == null) {
            return Collections.emptyMap();
        }
        PluginService pluginService = SpringContextUtil.getBean(PluginService.class);
        Plugin plugin = pluginService.getById(tool.getPluginId());
        if (plugin != null && PluginType.isWorkflow(plugin.getType())) {
            return executeWorkflowPlugin(chain, map, plugin);
        }
        Tool function = tool.toFunction();
        if (function == null) {
            return Collections.emptyMap();
        }

        Object result = function.invoke(map);
        if (result == null) {
            return Collections.emptyMap();
        }

        if (result instanceof Map) {
            return (Map<String, Object>) result;
        }

        return JSON.parseObject(JSON.toJSONString(result), Map.class);
    }

    private Map<String, Object> executeWorkflowPlugin(Chain chain, Map<String, Object> map, Plugin plugin) {
        WorkflowPluginAvailabilityService availabilityService =
                SpringContextUtil.getBean(WorkflowPluginAvailabilityService.class);
        LoginAccount operator = WorkFlowUtil.getOperator(chain);
        WorkflowPluginAvailabilityDecision decision = availabilityService.evaluate(plugin, operator);
        if (!decision.isAvailable()) {
            return buildSkippedResult(decision);
        }
        WorkflowService workflowService = SpringContextUtil.getBean(WorkflowService.class);
        Workflow workflow = workflowService.getPublishedById(plugin.getWorkflowId());
        if (workflow == null) {
            return buildSkippedResult(decision);
        }
        WorkflowPluginSnapshotResolver snapshotResolver = SpringContextUtil.getBean(WorkflowPluginSnapshotResolver.class);
        Map<String, Object> workflowVariables = new LinkedHashMap<>();
        workflowVariables.put(Constants.LOGIN_USER_KEY, operator);
        if (map != null && !map.isEmpty()) {
            workflowVariables.putAll(map);
        }
        Object result = snapshotResolver.buildWorkflowTool(workflow).invoke(workflowVariables);
        if (result == null) {
            return Collections.emptyMap();
        }
        if (result instanceof Map<?, ?> resultMap) {
            return (Map<String, Object>) resultMap;
        }
        return JSON.parseObject(JSON.toJSONString(result), Map.class);
    }

    private Map<String, Object> buildSkippedResult(WorkflowPluginAvailabilityDecision decision) {
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("skipped", true);
        result.put("reasonCode", decision.getReasonCode());
        result.put("reasonMessage", decision.getReasonMessage());
        return result;
    }

    public BigInteger getPluginId() {
        return pluginId;
    }

    public void setPluginId(BigInteger pluginId) {
        this.pluginId = pluginId;
    }
}
