package tech.easyflow.approval.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.approval.entity.ApprovalInstance;
import tech.easyflow.approval.entity.ApprovalFlowStep;
import tech.easyflow.approval.entity.ApprovalLog;
import tech.easyflow.approval.entity.ApprovalTask;
import tech.easyflow.approval.entity.vo.ApprovalAssigneeTargetVo;
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
import tech.easyflow.approval.service.ApprovalActionFacade;
import tech.easyflow.approval.service.ApprovalAssigneeService;
import tech.easyflow.approval.enums.ApprovalAssigneeType;
import tech.easyflow.approval.enums.ApprovalEventType;
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
import tech.easyflow.approval.enums.ApprovalTaskStatus;
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
import tech.easyflow.approval.mapper.ApprovalLogMapper;
import tech.easyflow.approval.mapper.ApprovalTaskMapper;
import tech.easyflow.approval.service.ApprovalInstanceService;
import tech.easyflow.approval.service.ApprovalMatchService;
import tech.easyflow.common.constant.enums.EnumDataStatus;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.entity.SysDept;
import tech.easyflow.system.service.SysAccountService;
import tech.easyflow.system.service.SysDeptService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 审批实例服务实现。
 */
@Service
public class ApprovalInstanceServiceImpl implements ApprovalInstanceService {

    @Resource
    private ApprovalMatchService approvalMatchService;

    @Resource
    private ApprovalInstanceMapper approvalInstanceMapper;

    @Resource
    private ApprovalTaskMapper approvalTaskMapper;

    @Resource
    private ApprovalLogMapper approvalLogMapper;

    @Resource
    private ApprovalFlowStepMapper approvalFlowStepMapper;

    @Resource
    private ApprovalAssigneeService approvalAssigneeService;

    @Resource
    private SysAccountService sysAccountService;

    @Resource
    private SysDeptService sysDeptService;

    @Lazy
    @Resource
    private ApprovalActionFacade approvalActionFacade;

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BigInteger submitApproval(ApprovalSubmitRequest request) {
        if (request == null) {
            throw new BusinessException("审批请求不能为空");
        }
        if (request.getApplicantId() == null) {
            throw new BusinessException("申请人不能为空");
        }
        LoginAccount loginAccount = requireCurrentLoginAccount();
        if (!loginAccount.getId().equals(request.getApplicantId())) {
            throw new BusinessException(403, 403, "不允许以其他账号身份提交审批");
        }
        SysAccount applicant = requireTenantAccount(request.getApplicantId(), "申请人");
        if (!loginAccount.getTenantId().equals(applicant.getTenantId())) {
            throw new BusinessException(403, 403, "申请人租户信息与当前登录态不一致");
        }
        ApprovalFlowDetailVo flow = approvalMatchService.matchFlow(request);
        if (CollectionUtil.isEmpty(flow.getSteps())) {
            throw new BusinessException("审批流程未配置步骤");
        }

        List<ApprovalFlowStepVo> steps = new ArrayList<>(flow.getSteps());
        steps.sort(Comparator.comparing(ApprovalFlowStepVo::getStepNo));
        SysDept applicantDept = resolveApplicantDepartment(steps, applicant);
        if (applicantDept != null) {
            // 在创建任何实例或任务前确认每个受限步骤在发起人直属部门内都有候选账号。
            approvalAssigneeService.validateRestrictedStepCandidates(steps, applicantDept.getId());
        }
        ApprovalFlowStepVo firstStep = steps.get(0);
        Date now = new Date();

        ApprovalInstance instance = new ApprovalInstance();
        instance.setTenantId(applicant.getTenantId());
        instance.setFlowId(flow.getId());
        instance.setFlowVersion(flow.getVersion());
        instance.setResourceType(flow.getResourceType());
        instance.setResourceId(request.getResourceId());
        instance.setActionType(flow.getActionType());
        instance.setStatus(ApprovalInstanceStatus.PENDING.getCode());
        instance.setCurrentStepNo(firstStep.getStepNo());
        instance.setSnapshotJson(buildInstanceSnapshot(request, flow, steps, applicantDept));
        instance.setSummary(request.getSummary());
        instance.setApplicationReason(request.getApplicationReason());
        instance.setApplicantId(request.getApplicantId());
        if (applicantDept != null) {
            instance.setApplicantDeptId(applicantDept.getId());
            instance.setApplicantDeptName(applicantDept.getDeptName());
        }
        instance.setSubmittedAt(now);
        instance.setCreated(now);
        instance.setCreatedBy(request.getApplicantId());
        instance.setModified(now);
        instance.setModifiedBy(request.getApplicantId());
        approvalInstanceMapper.insert(instance);

        createTask(instance.getId(), firstStep, instance.getApplicantDeptId(), request.getApplicantId(), now);
        Map<String, Object> submittedPayload = new LinkedHashMap<>();
        submittedPayload.put("flowId", flow.getId());
        submittedPayload.put("flowVersion", flow.getVersion());
        submittedPayload.put("summary", request.getSummary());
        if (request.getApplicationReason() != null) {
            submittedPayload.put("applicationReason", request.getApplicationReason());
        }
        appendLog(instance.getId(), ApprovalEventType.SUBMITTED.getCode(), request.getApplicantId(),
                submittedPayload, now);
        appendLog(instance.getId(), ApprovalEventType.STEP_CREATED.getCode(), request.getApplicantId(),
                buildStepCreatedPayload(firstStep), now);
        return instance.getId();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(BigInteger instanceId, String comment, BigInteger operatorId) {
        ApprovalInstance instance = requireActiveInstance(instanceId, operatorId);
        ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
        assertTaskOperable(currentTask, operatorId);
        List<ApprovalFlowStepVo> steps = resolveFrozenSteps(instance);

        Date now = new Date();
        int currentIndex = findCurrentStepIndex(steps, instance.getCurrentStepNo());
        ApprovalFlowStepVo nextStep = currentIndex == steps.size() - 1 ? null : steps.get(currentIndex + 1);
        if (nextStep != null && Integer.valueOf(1).equals(nextStep.getRestrictToApplicantDept())) {
            if (instance.getApplicantDeptId() == null) {
                throw new BusinessException("审批实例缺少冻结的发起人部门");
            }
            // 先校验下一步候选人，校验失败时当前待办保持未处理。
            approvalAssigneeService.validateRestrictedStepCandidates(
                    List.of(nextStep), instance.getApplicantDeptId());
        }
        finishTask(currentTask, ApprovalTaskStatus.APPROVED.getCode(), comment, operatorId, now);
        if (currentIndex == steps.size() - 1) {
            instance.setStatus(ApprovalInstanceStatus.APPROVED.getCode());
            instance.setFinishedAt(now);
        } else {
            instance.setStatus(ApprovalInstanceStatus.PROCESSING.getCode());
            instance.setCurrentStepNo(nextStep.getStepNo());
            createTask(instance.getId(), nextStep, instance.getApplicantDeptId(), operatorId, now);
            appendLog(instance.getId(), ApprovalEventType.STEP_CREATED.getCode(), operatorId,
                    buildStepCreatedPayload(nextStep), now);
        }
        instance.setModified(now);
        instance.setModifiedBy(operatorId);
        approvalInstanceMapper.update(instance);
        appendLog(instance.getId(), ApprovalEventType.APPROVED.getCode(), operatorId, Map.of(
                "stepNo", currentTask.getStepNo(),
                "comment", comment == null ? "" : comment
        ), now);
        if (ApprovalInstanceStatus.from(instance.getStatus()).isFinished()) {
            approvalActionFacade.handleApproved(instance, operatorId, comment);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(BigInteger instanceId, String comment, BigInteger operatorId) {
        ApprovalInstance instance = requireActiveInstance(instanceId, operatorId);
        ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
        assertTaskOperable(currentTask, operatorId);
        Date now = new Date();
        finishTask(currentTask, ApprovalTaskStatus.REJECTED.getCode(), comment, operatorId, now);
        instance.setStatus(ApprovalInstanceStatus.REJECTED.getCode());
        instance.setFinishedAt(now);
        instance.setModified(now);
        instance.setModifiedBy(operatorId);
        approvalInstanceMapper.update(instance);
        appendLog(instance.getId(), ApprovalEventType.REJECTED.getCode(), operatorId, Map.of(
                "stepNo", currentTask.getStepNo(),
                "comment", comment == null ? "" : comment
        ), now);
        approvalActionFacade.handleRejected(instance, operatorId, comment);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(BigInteger instanceId, String comment, BigInteger operatorId) {
        ApprovalInstance instance = requireActiveInstance(instanceId, operatorId);
        // 撤回属于发起人的自助操作，不能沿用审批任务处理人的授权口径。
        if (!Objects.equals(instance.getApplicantId(), operatorId)) {
            throw new BusinessException(403, 403, "仅审批申请人可以撤回该请求");
        }
        ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
        Date now = new Date();
        finishTask(currentTask, ApprovalTaskStatus.REVOKED.getCode(), comment, operatorId, now);
        instance.setStatus(ApprovalInstanceStatus.REVOKED.getCode());
        instance.setFinishedAt(now);
        instance.setModified(now);
        instance.setModifiedBy(operatorId);
        approvalInstanceMapper.update(instance);
        appendLog(instance.getId(), ApprovalEventType.REVOKED.getCode(), operatorId, Map.of(
                "stepNo", currentTask.getStepNo(),
                "comment", comment == null ? "" : comment
        ), now);
        approvalActionFacade.handleRevoked(instance, operatorId, comment);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsActiveInstance(String resourceType, BigInteger resourceId) {
        BigInteger tenantId = requireCurrentTenantId();
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(ApprovalInstance::getTenantId, tenantId)
                .eq(ApprovalInstance::getResourceType, resourceType)
                .eq(ApprovalInstance::getResourceId, resourceId)
                .notIn(ApprovalInstance::getStatus,
                        ApprovalInstanceStatus.APPROVED.getCode(),
                        ApprovalInstanceStatus.REJECTED.getCode(),
                        ApprovalInstanceStatus.REVOKED.getCode());
        return approvalInstanceMapper.selectCountByQuery(queryWrapper) > 0;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ApprovalInstance getById(BigInteger instanceId) {
        return approvalInstanceMapper.selectOneByQuery(QueryWrapper.create()
                .eq(ApprovalInstance::getId, instanceId)
                .eq(ApprovalInstance::getTenantId, requireCurrentTenantId()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isLatestResourceInstance(BigInteger instanceId,
                                            String resourceType,
                                            BigInteger resourceId) {
        return approvalInstanceMapper.selectCountByQuery(QueryWrapper.create()
                .eq(ApprovalInstance::getTenantId, requireCurrentTenantId())
                .eq(ApprovalInstance::getResourceType, resourceType)
                .eq(ApprovalInstance::getResourceId, resourceId)
                .gt(ApprovalInstance::getId, instanceId)) == 0;
    }

    private Map<String, Object> buildInstanceSnapshot(ApprovalSubmitRequest request, ApprovalFlowDetailVo flow,
                                                      List<ApprovalFlowStepVo> steps, SysDept applicantDept) {
        Map<String, Object> snapshot = new LinkedHashMap<>();
        if (request.getSnapshotJson() != null) {
            snapshot.putAll(request.getSnapshotJson());
        }
        snapshot.put("categoryId", request.getCategoryId());
        snapshot.put("deptId", request.getDeptId());
        snapshot.put("flowId", flow.getId());
        snapshot.put("flowVersion", flow.getVersion());
        if (applicantDept != null) {
            snapshot.put("applicantDeptId", applicantDept.getId());
            snapshot.put("applicantDeptName", applicantDept.getDeptName());
        }
        if (request.getApplicationReason() != null) {
            snapshot.put("applicationReason", request.getApplicationReason());
        }
        snapshot.put("steps", steps.stream().map(item -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("stepNo", item.getStepNo());
            map.put("stepName", item.getStepName());
            map.put("assigneeType", item.getAssigneeType());
            map.put("assigneeTargetId", item.getAssigneeTargetId());
            map.put("assigneeTargetCode", item.getAssigneeTargetCode());
            map.put("assigneeTargetName", item.getAssigneeTargetName());
            map.put("restrictToApplicantDept",
                    Integer.valueOf(1).equals(item.getRestrictToApplicantDept()) ? 1 : 0);
            map.put("assigneeTargets", resolveStepTargets(item).stream().map(target -> {
                Map<String, Object> targetMap = new LinkedHashMap<>();
                targetMap.put("targetId", target.getTargetId());
                targetMap.put("targetCode", target.getTargetCode());
                targetMap.put("targetName", target.getTargetName());
                targetMap.put("includeChildren", target.getIncludeChildren());
                return targetMap;
            }).collect(Collectors.toList()));
            return map;
        }).collect(Collectors.toList()));
        return snapshot;
    }

    private List<ApprovalFlowStepVo> resolveFrozenSteps(ApprovalInstance instance) {
        if (instance.getSnapshotJson() == null) {
            throw new BusinessException("审批实例缺少流程快照");
        }
        Object value = instance.getSnapshotJson().get("steps");
        if (!(value instanceof List<?> steps)) {
            throw new BusinessException("审批实例缺少冻结步骤数据");
        }
        List<ApprovalFlowStepVo> result = new ArrayList<>();
        for (Object step : steps) {
            if (!(step instanceof Map<?, ?> stepMap)) {
                continue;
            }
            Object stepNo = stepMap.get("stepNo");
            Object stepName = stepMap.get("stepName");
            if (!(stepNo instanceof Number) || stepName == null) {
                continue;
            }
            ApprovalFlowStepVo stepVo = new ApprovalFlowStepVo();
            stepVo.setStepNo(((Number) stepNo).intValue());
            stepVo.setStepName(String.valueOf(stepName));
            Object assigneeType = stepMap.get("assigneeType");
            Object assigneeTargetId = stepMap.get("assigneeTargetId");
            Object assigneeTargetCode = stepMap.get("assigneeTargetCode");
            Object assigneeTargetName = stepMap.get("assigneeTargetName");
            if (assigneeType instanceof String type) {
                stepVo.setAssigneeType(type);
            }
            if (assigneeTargetId instanceof Number number) {
                stepVo.setAssigneeTargetId(BigInteger.valueOf(number.longValue()));
            } else if (assigneeTargetId instanceof String string && !string.isBlank()) {
                stepVo.setAssigneeTargetId(new BigInteger(string));
            }
            if (assigneeTargetCode != null) {
                stepVo.setAssigneeTargetCode(String.valueOf(assigneeTargetCode));
            }
            if (assigneeTargetName != null) {
                stepVo.setAssigneeTargetName(String.valueOf(assigneeTargetName));
            }
            stepVo.setRestrictToApplicantDept(parseEnabledFlag(stepMap.get("restrictToApplicantDept")));
            stepVo.setAssigneeTargets(parseAssigneeTargets(stepMap.get("assigneeTargets")));
            if (stepVo.getAssigneeTargets().isEmpty() && stepVo.getAssigneeTargetId() != null) {
                stepVo.setAssigneeTargets(List.of(legacyTarget(stepVo)));
            }
            result.add(stepVo);
        }
        mergeStepAssigneeFromFlow(instance.getFlowId(), result);
        result.sort(Comparator.comparing(ApprovalFlowStepVo::getStepNo));
        if (result.isEmpty()) {
            throw new BusinessException("审批实例缺少冻结步骤数据");
        }
        return result;
    }

    private void createTask(BigInteger instanceId, ApprovalFlowStepVo step, BigInteger applicantDeptId,
                            BigInteger operatorId, Date now) {
        List<ApprovalAssigneeTargetVo> targets = resolveStepTargets(step);
        if (CollectionUtil.isEmpty(targets)) {
            throw new BusinessException("审批步骤缺少审批对象");
        }
        step.setAssigneeTargets(targets);
        ApprovalAssigneeTargetVo primaryTarget = targets.get(0);
        ApprovalTask task = new ApprovalTask();
        task.setInstanceId(instanceId);
        task.setStepNo(step.getStepNo());
        task.setStatus(ApprovalTaskStatus.PENDING.getCode());
        task.setAssigneeRoleCode(ApprovalAssigneeType.ROLE.getCode().equals(step.getAssigneeType())
                ? primaryTarget.getTargetCode()
                : null);
        task.setAssigneeType(step.getAssigneeType());
        task.setAssigneeTargetId(primaryTarget.getTargetId());
        task.setAssigneeTargetCode(primaryTarget.getTargetCode());
        task.setAssigneeTargetName(primaryTarget.getTargetName());
        if (Integer.valueOf(1).equals(step.getRestrictToApplicantDept())) {
            if (applicantDeptId == null) {
                throw new BusinessException("审批任务缺少冻结的发起人部门");
            }
            task.setRequiredDeptId(applicantDeptId);
        }
        task.setCreated(now);
        task.setCreatedBy(operatorId);
        task.setModified(now);
        task.setModifiedBy(operatorId);
        approvalTaskMapper.insert(task);
        approvalAssigneeService.saveTaskAssigneeTargets(
                task.getId(), step.getAssigneeType(), targets, operatorId, now);
    }

    private void finishTask(ApprovalTask task, String status, String comment, BigInteger operatorId, Date now) {
        task.setStatus(status);
        task.setComment(comment);
        task.setActedBy(operatorId);
        task.setActedAt(now);
        task.setModified(now);
        task.setModifiedBy(operatorId);
        approvalTaskMapper.update(task);
    }

    private void appendLog(BigInteger instanceId, String eventType, BigInteger operatorId, Map<String, Object> payload, Date now) {
        ApprovalLog log = new ApprovalLog();
        log.setInstanceId(instanceId);
        log.setEventType(eventType);
        log.setOperatorId(operatorId);
        log.setPayloadJson(new LinkedHashMap<>(payload));
        log.setCreated(now);
        log.setCreatedBy(operatorId);
        log.setModified(now);
        log.setModifiedBy(operatorId);
        approvalLogMapper.insert(log);
    }

    private ApprovalInstance requireActiveInstance(BigInteger instanceId, BigInteger operatorId) {
        if (instanceId == null) {
            throw new BusinessException("审批实例ID不能为空");
        }
        SysAccount operator = requireTenantAccount(operatorId, "操作人");
        ApprovalInstance instance = approvalInstanceMapper.selectOneByQuery(QueryWrapper.create()
                .eq(ApprovalInstance::getId, instanceId)
                .eq(ApprovalInstance::getTenantId, operator.getTenantId())
                .forUpdate());
        if (instance == null) {
            throw new BusinessException(404, 404, "审批实例不存在");
        }
        if (ApprovalInstanceStatus.from(instance.getStatus()).isFinished()) {
            throw new BusinessException("审批实例已结束，无法继续处理");
        }
        return instance;
    }

    /**
     * 读取账号及其稳定租户归属。
     *
     * @param accountId 账号 ID
     * @param accountLabel 账号角色说明
     * @return 有效账号
     * @throws BusinessException 账号不存在或缺少租户归属时抛出
     */
    private SysAccount requireTenantAccount(BigInteger accountId, String accountLabel) {
        if (accountId == null) {
            throw new BusinessException(accountLabel + "不能为空");
        }
        SysAccount account = sysAccountService.getById(accountId);
        if (account == null || account.getTenantId() == null) {
            throw new BusinessException(403, 403, accountLabel + "不存在或租户信息无效");
        }
        return account;
    }

    /**
     * 获取当前登录账号的租户 ID。
     *
     * @return 当前租户 ID
     * @throws BusinessException 登录态缺少账号或租户信息时抛出
     */
    private BigInteger requireCurrentTenantId() {
        return requireCurrentLoginAccount().getTenantId();
    }

    /**
     * 获取完整的当前登录账号。
     *
     * @return 当前登录账号
     * @throws BusinessException 登录态缺少账号或租户信息时抛出
     */
    private LoginAccount requireCurrentLoginAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getId() == null || account.getTenantId() == null) {
            throw new BusinessException(401, 401, "未登录或登录态无效");
        }
        return account;
    }

    private ApprovalTask requireCurrentTask(BigInteger instanceId, Integer stepNo) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(ApprovalTask::getInstanceId, instanceId)
                .eq(ApprovalTask::getStepNo, stepNo)
                .eq(ApprovalTask::getStatus, ApprovalTaskStatus.PENDING.getCode())
                .forUpdate();
        ApprovalTask task = approvalTaskMapper.selectOneByQuery(queryWrapper);
        if (task == null) {
            throw new BusinessException("当前审批任务不存在");
        }
        return task;
    }

    private int findCurrentStepIndex(List<ApprovalFlowStepVo> steps, Integer currentStepNo) {
        for (int i = 0; i < steps.size(); i++) {
            if (steps.get(i).getStepNo().equals(currentStepNo)) {
                return i;
            }
        }
        throw new BusinessException("审批流程步骤不存在");
    }

    /**
     * 校验当前操作人是否命中审批任务。
     *
     * @param task 审批任务
     * @param operatorId 当前操作人
     */
    private void assertTaskOperable(ApprovalTask task, BigInteger operatorId) {
        if (!approvalAssigneeService.canHandleTask(task, operatorId, approvalAssigneeService.getAvailableRoleIds(operatorId))) {
            throw new BusinessException("当前用户无权处理该审批任务");
        }
    }

    /**
     * 为历史审批实例补齐流程步骤里的审批对象信息。
     *
     * @param flowId 流程ID
     * @param steps 冻结步骤
     */
    private void mergeStepAssigneeFromFlow(BigInteger flowId, List<ApprovalFlowStepVo> steps) {
        List<ApprovalFlowStep> storedSteps = approvalFlowStepMapper.selectListByQuery(
                QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId));
        if (CollectionUtil.isEmpty(storedSteps)) {
            return;
        }
        Map<Integer, ApprovalFlowStep> storedMap = storedSteps.stream().collect(Collectors.toMap(
                ApprovalFlowStep::getStepNo,
                item -> item,
                (left, right) -> left,
                LinkedHashMap::new
        ));
        Map<BigInteger, List<ApprovalAssigneeTargetVo>> targetMap =
                approvalAssigneeService.loadStepAssigneeTargets(storedSteps);
        for (ApprovalFlowStepVo step : steps) {
            if (CollectionUtil.isNotEmpty(step.getAssigneeTargets())
                    && step.getAssigneeTargetId() != null
                    && step.getAssigneeType() != null) {
                continue;
            }
            ApprovalFlowStep storedStep = storedMap.get(step.getStepNo());
            if (storedStep == null) {
                continue;
            }
            step.setAssigneeType(storedStep.getAssigneeType());
            step.setAssigneeTargetId(storedStep.getAssigneeTargetId());
            step.setAssigneeTargetCode(storedStep.getAssigneeTargetCode());
            step.setAssigneeTargetName(storedStep.getAssigneeTargetName());
            step.setAssigneeTargets(targetMap.getOrDefault(storedStep.getId(), List.of()));
        }
    }

    /**
     * 组装步骤创建日志载荷。
     *
     * @param step 步骤信息
     * @return 日志载荷
     */
    private Map<String, Object> buildStepCreatedPayload(ApprovalFlowStepVo step) {
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("stepNo", step.getStepNo());
        payload.put("stepName", step.getStepName());
        payload.put("assigneeType", step.getAssigneeType());
        payload.put("assigneeTargetId", step.getAssigneeTargetId());
        payload.put("assigneeTargetCode", step.getAssigneeTargetCode());
        payload.put("assigneeTargetName", step.getAssigneeTargetName());
        payload.put("assigneeTargets", resolveStepTargets(step));
        payload.put("restrictToApplicantDept",
                Integer.valueOf(1).equals(step.getRestrictToApplicantDept()) ? 1 : 0);
        return payload;
    }

    /**
     * 解析受限步骤提交时需要冻结的发起人直属部门。
     *
     * @param steps 流程步骤
     * @param applicant 发起人账号
     * @return 有受限步骤时返回有效部门，否则返回空
     */
    private SysDept resolveApplicantDepartment(List<ApprovalFlowStepVo> steps, SysAccount applicant) {
        boolean restricted = steps.stream()
                .anyMatch(step -> Integer.valueOf(1).equals(step.getRestrictToApplicantDept()));
        if (!restricted) {
            return null;
        }
        if (applicant.getDeptId() == null) {
            throw new BusinessException("发起人未归属部门，无法提交当前审批");
        }
        SysDept dept = sysDeptService.getById(applicant.getDeptId());
        if (dept == null || !EnumDataStatus.AVAILABLE.getCode().equals(dept.getStatus())) {
            throw new BusinessException("发起人部门不存在或未启用");
        }
        return dept;
    }

    /**
     * 从冻结快照中解析审批对象列表。
     *
     * @param value 快照字段
     * @return 审批对象列表
     */
    private List<ApprovalAssigneeTargetVo> parseAssigneeTargets(Object value) {
        if (!(value instanceof List<?> values)) {
            return List.of();
        }
        List<ApprovalAssigneeTargetVo> result = new ArrayList<>();
        for (Object item : values) {
            if (!(item instanceof Map<?, ?> map)) {
                continue;
            }
            BigInteger targetId = parseTargetId(map.get("targetId"));
            if (targetId == null) {
                continue;
            }
            ApprovalAssigneeTargetVo target = new ApprovalAssigneeTargetVo();
            target.setTargetId(targetId);
            target.setTargetCode(map.get("targetCode") == null ? null : String.valueOf(map.get("targetCode")));
            target.setTargetName(map.get("targetName") == null ? null : String.valueOf(map.get("targetName")));
            Object includeChildren = map.get("includeChildren");
            target.setIncludeChildren(includeChildren instanceof Number number && number.intValue() == 1
                    || Boolean.TRUE.equals(includeChildren) ? 1 : 0);
            result.add(target);
        }
        return result;
    }

    /**
     * 解析快照中的对象 ID。
     *
     * @param value 快照值
     * @return 对象 ID
     */
    private BigInteger parseTargetId(Object value) {
        if (value instanceof BigInteger bigInteger) {
            return bigInteger;
        }
        if (value instanceof Number number) {
            return new BigInteger(number.toString());
        }
        if (value instanceof String string && !string.isBlank()) {
            return new BigInteger(string);
        }
        return null;
    }

    /**
     * 解析快照中的开关标记，旧快照缺失时默认关闭。
     *
     * @param value 快照值
     * @return 1 表示开启，0 表示关闭
     */
    private int parseEnabledFlag(Object value) {
        return value instanceof Number number && number.intValue() == 1
                || Boolean.TRUE.equals(value)
                || "1".equals(value)
                || "true".equalsIgnoreCase(String.valueOf(value))
                ? 1
                : 0;
    }

    /**
     * 将旧单值步骤转换为单元素审批对象。
     *
     * @param step 旧步骤
     * @return 审批对象
     */
    private ApprovalAssigneeTargetVo legacyTarget(ApprovalFlowStepVo step) {
        ApprovalAssigneeTargetVo target = new ApprovalAssigneeTargetVo();
        target.setTargetId(step.getAssigneeTargetId());
        target.setTargetCode(step.getAssigneeTargetCode());
        target.setTargetName(step.getAssigneeTargetName());
        target.setIncludeChildren(0);
        return target;
    }

    /**
     * 获取步骤审批对象，兼容旧单值步骤。
     *
     * @param step 审批步骤
     * @return 审批对象列表
     */
    private List<ApprovalAssigneeTargetVo> resolveStepTargets(ApprovalFlowStepVo step) {
        if (CollectionUtil.isNotEmpty(step.getAssigneeTargets())) {
            return step.getAssigneeTargets();
        }
        return step.getAssigneeTargetId() == null ? List.of() : List.of(legacyTarget(step));
    }
}
