package tech.easyflow.usercenter.controller.ai;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import org.springframework.web.bind.annotation.*;
import tech.easyflow.ai.permission.WorkflowVisibilityQueryHelper;
import tech.easyflow.ai.easyagentsflow.entity.ChainInfo;
import tech.easyflow.ai.easyagentsflow.entity.NodeInfo;
import tech.easyflow.ai.easyagentsflow.entity.WorkflowCheckStage;
import tech.easyflow.ai.easyagentsflow.service.TinyFlowService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowCheckService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowRunningParameterResolver;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.common.annotation.UsePermission;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.enums.ResourceLookup;
import tech.easyflow.system.permission.resource.RequireResourceAccess;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 工作流
 */
@RestController
@RequestMapping("/userCenter/workflow")
@UsePermission(moduleName = "/api/v1/workflow")
public class UcWorkflowController extends BaseCurdController<WorkflowService, Workflow> {

    @Resource
    private ChainExecutor chainExecutor;
    @Resource
    private TinyFlowService tinyFlowService;
    @Resource
    private WorkflowCheckService workflowCheckService;
    @Resource
    private WorkflowRunningParameterResolver workflowRunningParameterResolver;
    @Resource
    private WorkflowVisibilityQueryHelper workflowVisibilityQueryHelper;

    public UcWorkflowController(WorkflowService service) {
        super(service);
    }

    /**
     * 节点单独运行
     */
    @PostMapping("/singleRun")
    @SaCheckPermission("/api/v1/workflow/save")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.USE,
            lookup = ResourceLookup.WORKFLOW_ID,
            idExpr = "#workflowId",
            denyMessage = "无权限运行工作流"
    )
    public Result<?> singleRun(
            @JsonBody(value = "workflowId", required = true) BigInteger workflowId,
            @JsonBody(value = "nodeId", required = true) String nodeId,
            @JsonBody("variables") Map<String, Object> variables) {

        Workflow workflow = service.getById(workflowId);
        if (workflow == null) {
            return Result.fail(1, "工作流不存在");
        }
        if (variables == null) {
            variables = new HashMap<>();
        }
        variables = workflowRunningParameterResolver.normalizeRuntimeVariables(workflow.getContent(), variables);
        if (StpUtil.isLogin()) {
            variables.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
        }
        Map<String, Object> res = chainExecutor.executeNode(workflowId.toString(), nodeId, variables);
        return Result.ok(res);
    }

    /**
     * 运行工作流 - v2
     */
    @PostMapping("/runAsync")
    @SaCheckPermission("/api/v1/workflow/save")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.USE,
            lookup = ResourceLookup.WORKFLOW_ID,
            idExpr = "#id",
            denyMessage = "无权限运行工作流"
    )
    public Result<String> runAsync(@JsonBody(value = "id", required = true) BigInteger id,
                                   @JsonBody("variables") Map<String, Object> variables) {
        if (variables == null) {
            variables = new HashMap<>();
        }
        Workflow workflow = service.getById(id);
        if (workflow == null) {
            throw new RuntimeException("工作流不存在");
        }
        workflowCheckService.checkOrThrow(workflow.getContent(), WorkflowCheckStage.PRE_EXECUTE, workflow.getId());
        variables = workflowRunningParameterResolver.normalizeRuntimeVariables(workflow.getContent(), variables);
        if (StpUtil.isLogin()) {
            variables.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
        }
        String executeId = chainExecutor.executeAsync(id.toString(), variables);
        return Result.ok(executeId);
    }

    /**
     * 获取工作流运行状态 - v2
     */
    @PostMapping("/getChainStatus")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.USE,
            lookup = ResourceLookup.EXEC_KEY,
            idExpr = "#executeId",
            denyMessage = "无权限访问该执行记录"
    )
    public Result<ChainInfo> getChainStatus(@JsonBody(value = "executeId") String executeId,
                                            @JsonBody("nodes") List<NodeInfo> nodes) {
        ChainInfo res = tinyFlowService.getChainStatus(executeId, nodes);
        return Result.ok(res);
    }

    /**
     * 恢复工作流运行 - v2
     */
    @PostMapping("/resume")
    @SaCheckPermission("/api/v1/workflow/save")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.USE,
            lookup = ResourceLookup.EXEC_KEY,
            idExpr = "#executeId",
            denyMessage = "无权限恢复工作流执行"
    )
    public Result<Void> resume(@JsonBody(value = "executeId", required = true) String executeId,
                               @JsonBody("confirmParams") Map<String, Object> confirmParams) {
        chainExecutor.resumeAsync(executeId, confirmParams);
        return Result.ok();
    }

    /**
     * 获取工作流参数 - v2
     */
    @GetMapping("getRunningParameters")
    @SaCheckPermission("/api/v1/workflow/query")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.READ,
            lookup = ResourceLookup.WORKFLOW_ID,
            idExpr = "#id",
            denyMessage = "无权限访问工作流"
    )
    public Result<?> getRunningParameters(@RequestParam BigInteger id) {
        Workflow workflow = service.getById(id);

        if (workflow == null) {
            return Result.fail(1, "can not find the workflow by id: " + id);
        }
        workflowCheckService.checkOrThrow(workflow.getContent(), WorkflowCheckStage.PRE_EXECUTE, workflow.getId());
        Map<String, Object> res = workflowRunningParameterResolver.buildRunningParametersView(workflow);
        if (res == null) {
            return Result.fail(2, "节点配置错误，请检查! ");
        }
        return Result.ok(res);
    }

    @Override
    protected Result<?> onSaveOrUpdateBefore(Workflow entity, boolean isSave) {
        return Result.fail("-");
    }

    @Override
    protected Result<?> onRemoveBefore(Collection<Serializable> ids) {
        return Result.fail("-");
    }

    @Override
    public Result<List<Workflow>> list(Workflow entity, Boolean asTree, String sortKey, String sortType) {
        QueryWrapper queryWrapper = QueryWrapper.create(entity, buildOperators(entity));
        workflowVisibilityQueryHelper.applyReadableAccess(queryWrapper);
        queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
        return Result.ok(service.list(queryWrapper));
    }

    @Override
    protected Page<Workflow> queryPage(Page<Workflow> page, QueryWrapper queryWrapper) {
        workflowVisibilityQueryHelper.applyReadableAccess(queryWrapper);
        return super.queryPage(page, queryWrapper);
    }

    @Override
    @GetMapping("detail")
    @RequireResourceAccess(
            resource = CategoryResourceType.WORKFLOW,
            action = ResourceAction.READ,
            lookup = ResourceLookup.WORKFLOW_ID,
            idExpr = "#id",
            denyMessage = "无权限访问工作流"
    )
    public Result<Workflow> detail(String id) {
        Workflow workflow = service.getDetail(id);
        return Result.ok(workflow);
    }
}
