package tech.easyflow.publicapi.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotBlank;
import org.springframework.web.bind.annotation.*;
import tech.easyflow.approval.annotation.RequirePublishedAccess;
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.easyagentsflow.support.PublishedWorkflowDefinitionIds;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.entity.WorkflowExecResult;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.WorkflowExecResultService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowApiPermissionService;
import tech.easyflow.ai.utils.WorkFlowUtil;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.system.entity.SysApiKey;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 工作流
 */
@RequestMapping("/public-api/workflow")
@RestController
public class PublicWorkflowController {

    @Resource
    private WorkflowService workflowService;
    @Resource
    private ChainExecutor chainExecutor;
    @Resource
    private TinyFlowService tinyFlowService;
    @Resource
    private WorkflowCheckService workflowCheckService;
    @Resource
    private WorkflowRunningParameterResolver workflowRunningParameterResolver;
    @Resource
    private WorkflowApiPermissionService workflowApiPermissionService;
    @Resource
    private WorkflowExecResultService workflowExecResultService;

    /**
     * 通过id或别名获取工作流详情
     *
     * @param key id或者别名
     * @return 工作流详情
     */
    @GetMapping(value = "/getByIdOrAlias")
    @RequirePublishedAccess(resourceType = "WORKFLOW", idExpr = "#key", denyMessage = "工作流尚未发布")
    public Result<Workflow> getByIdOrAlias(
            @RequestParam
            @NotBlank(message = "key不能为空") String key,
            HttpServletRequest request) {
        workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
        Workflow workflow = workflowService.getPublishedDetail(key);
        assertStrictPublishedWorkflow(workflow);
        return Result.ok(workflow);
    }

    /**
     * 节点单独运行
     */
    @PostMapping("/singleRun")
    @SaCheckPermission("/api/v1/workflow/save")
    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 = workflowService.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")
    @RequirePublishedAccess(resourceType = "WORKFLOW", idExpr = "#id", denyMessage = "工作流尚未发布")
    public Result<String> runAsync(@JsonBody(value = "id", required = true) BigInteger id,
                                   @JsonBody("variables") Map<String, Object> variables,
                                   HttpServletRequest request) {
        SysApiKey apiKey = workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
        if (variables == null) {
            variables = new HashMap<>();
        }
        Workflow workflow = workflowService.getPublishedById(id);
        assertStrictPublishedWorkflow(workflow);
        workflowCheckService.checkOrThrow(workflow.getContent(), WorkflowCheckStage.PRE_EXECUTE, workflow.getId());
        variables = workflowRunningParameterResolver.normalizeRuntimeVariables(workflow.getContent(), variables);
        variables.put(Constants.LOGIN_USER_KEY, buildApiKeyLoginAccount(apiKey));
        variables.put(WorkFlowUtil.CREATED_KEY_MEMORY_KEY, WorkFlowUtil.API_KEY);
        String executeId = chainExecutor.executeAsync(PublishedWorkflowDefinitionIds.published(id.toString()), variables);
        return Result.ok(executeId);
    }

    /**
     * 获取工作流运行状态 - v2
     */
    @PostMapping("/getChainStatus")
    public Result<ChainInfo> getChainStatus(@JsonBody(value = "executeId") String executeId,
                                            @JsonBody("nodes") List<NodeInfo> nodes,
                                            HttpServletRequest request) {
        SysApiKey apiKey = workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
        assertApiKeyExecutionOwnership(apiKey, executeId);
        ChainInfo res = tinyFlowService.getChainStatus(executeId, nodes);
        return Result.ok(res);
    }

    /**
     * 恢复工作流运行 - v2
     */
    @PostMapping("/resume")
    public Result<Void> resume(@JsonBody(value = "executeId", required = true) String executeId,
                               @JsonBody("confirmParams") Map<String, Object> confirmParams,
                               HttpServletRequest request) {
        SysApiKey apiKey = workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
        WorkflowExecResult execResult = assertApiKeyExecutionOwnership(apiKey, executeId);
        assertWorkflowExecutionResumable(execResult);
        chainExecutor.resumeAsync(executeId, confirmParams);
        return Result.ok();
    }

    @GetMapping("getRunningParameters")
    @RequirePublishedAccess(resourceType = "WORKFLOW", idExpr = "#id", denyMessage = "工作流尚未发布")
    public Result<?> getRunningParameters(@RequestParam BigInteger id, HttpServletRequest request) {
        workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
        Workflow workflow = workflowService.getPublishedById(id);

        assertStrictPublishedWorkflow(workflow);
        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);
    }

    /**
     * 构建 API Key 调用方的运行身份。
     *
     * @param apiKey 访问令牌
     * @return 工作流运行身份
     */
    private LoginAccount buildApiKeyLoginAccount(SysApiKey apiKey) {
        LoginAccount account = new LoginAccount();
        account.setId(apiKey.getId());
        account.setDeptId(apiKey.getDeptId() == null ? BigInteger.ZERO : apiKey.getDeptId());
        account.setTenantId(apiKey.getTenantId() == null ? BigInteger.ZERO : apiKey.getTenantId());
        account.setLoginName("apikey:" + apiKey.getId());
        account.setNickname("API 调用方");
        return account;
    }

    /**
     * 校验工作流 Public API 只能访问严格已发布且存在发布快照的工作流。
     *
     * @param workflow 工作流发布视图
     */
    private void assertStrictPublishedWorkflow(Workflow workflow) {
        if (workflow == null || !PublishStatus.PUBLISHED.getCode().equals(workflow.getPublishStatus())
                || workflow.getPublishedSnapshotJson() == null || workflow.getPublishedSnapshotJson().isEmpty()) {
            throw new BusinessException("工作流尚未发布");
        }
    }

    /**
     * 校验 Public Workflow API 后续操作只能作用于当前 API Key 发起的执行实例。
     *
     * @param apiKey 当前 API Key
     * @param executeId 执行 ID
     * @return 已通过归属校验的执行记录
     */
    private WorkflowExecResult assertApiKeyExecutionOwnership(SysApiKey apiKey, String executeId) {
        if (executeId == null || executeId.isBlank()) {
            throw new BusinessException("执行ID不能为空");
        }
        WorkflowExecResult execResult = workflowExecResultService.getByExecKey(executeId);
        if (execResult == null) {
            throw new BusinessException("工作流执行记录不存在，请稍后重试");
        }
        if (!WorkFlowUtil.API_KEY.equals(execResult.getCreatedKey())
                || apiKey == null
                || apiKey.getId() == null
                || !String.valueOf(apiKey.getId()).equals(execResult.getCreatedBy())) {
            throw new BusinessException("无权限访问当前工作流执行记录");
        }
        return execResult;
    }

    /**
     * 校验当前执行实例是否仍允许恢复。
     *
     * @param execResult 执行记录
     */
    private void assertWorkflowExecutionResumable(WorkflowExecResult execResult) {
        if (execResult == null || execResult.getWorkflowId() == null) {
            throw new BusinessException("工作流执行记录不存在，请稍后重试");
        }
        Workflow workflow = workflowService.getById(execResult.getWorkflowId());
        if (workflow == null || !PublishStatus.PUBLISHED.getCode().equals(workflow.getPublishStatus())
                || workflow.getPublishedSnapshotJson() == null || workflow.getPublishedSnapshotJson().isEmpty()) {
            throw new BusinessException("工作流已下线或不可恢复执行");
        }
    }
}
