package tech.easyflow.approval.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.approval.entity.ApprovalFlow;
import tech.easyflow.approval.entity.ApprovalFlowScope;
import tech.easyflow.approval.entity.ApprovalFlowStep;
import tech.easyflow.approval.entity.ApprovalInstance;
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
import tech.easyflow.approval.entity.vo.ApprovalFlowPageVo;
import tech.easyflow.approval.entity.vo.ApprovalFlowScopeVo;
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
import tech.easyflow.approval.enums.ApprovalActionType;
import tech.easyflow.approval.enums.ApprovalFlowStatus;
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
import tech.easyflow.approval.enums.ApprovalResourceType;
import tech.easyflow.approval.enums.ApprovalScopeType;
import tech.easyflow.approval.mapper.ApprovalFlowMapper;
import tech.easyflow.approval.mapper.ApprovalFlowScopeMapper;
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
import tech.easyflow.approval.service.ApprovalAssigneeService;
import tech.easyflow.approval.service.ApprovalFlowService;

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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 审批流程服务实现。
 */
@Service
public class ApprovalFlowServiceImpl extends ServiceImpl<ApprovalFlowMapper, ApprovalFlow> implements ApprovalFlowService {

    @Resource
    private ApprovalFlowMapper approvalFlowMapper;

    @Resource
    private ApprovalFlowScopeMapper approvalFlowScopeMapper;

    @Resource
    private ApprovalFlowStepMapper approvalFlowStepMapper;

    @Resource
    private ApprovalInstanceMapper approvalInstanceMapper;

    @Resource
    private ApprovalAssigneeService approvalAssigneeService;

    /**
     * {@inheritDoc}
     */
    @Override
    public Page<ApprovalFlowPageVo> pageFlows(String name, String resourceType, String actionType, String status,
                                              Long pageNumber, Long pageSize) {
        long actualPageNumber = pageNumber == null || pageNumber < 1 ? 1L : pageNumber;
        long actualPageSize = pageSize == null || pageSize < 1 ? 10L : pageSize;
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (StringUtils.hasText(name)) {
            queryWrapper.like(ApprovalFlow::getName, name.trim());
        }
        if (StringUtils.hasText(resourceType)) {
            queryWrapper.eq(ApprovalFlow::getResourceType, ApprovalResourceType.from(resourceType).getCode());
        }
        if (StringUtils.hasText(actionType)) {
            queryWrapper.eq(ApprovalFlow::getActionType, ApprovalActionType.from(actionType).getCode());
        }
        if (StringUtils.hasText(status)) {
            queryWrapper.eq(ApprovalFlow::getStatus, ApprovalFlowStatus.from(status).getCode());
        }
        queryWrapper.orderBy("priority desc, modified desc, created asc");

        Page<ApprovalFlow> entityPage = page(new Page<>(actualPageNumber, actualPageSize), queryWrapper);
        List<ApprovalFlow> records = entityPage.getRecords();
        List<BigInteger> flowIds = records.stream().map(ApprovalFlow::getId).collect(Collectors.toList());

        Map<BigInteger, List<ApprovalFlowScope>> scopeMap = loadScopeMap(flowIds);
        Map<BigInteger, List<ApprovalFlowStep>> stepMap = loadStepMap(flowIds);
        Map<BigInteger, Long> pendingCountMap = loadPendingInstanceCountMap(flowIds);

        List<ApprovalFlowPageVo> result = new ArrayList<>();
        for (ApprovalFlow record : records) {
            ApprovalFlowPageVo item = new ApprovalFlowPageVo();
            item.setId(record.getId());
            item.setName(record.getName());
            item.setResourceType(record.getResourceType());
            item.setActionType(record.getActionType());
            item.setPriority(record.getPriority());
            item.setStatus(record.getStatus());
            item.setVersion(record.getVersion());
            item.setModified(record.getModified());
            item.setPendingInstanceCount(pendingCountMap.getOrDefault(record.getId(), 0L));
            item.setStepCount(stepMap.getOrDefault(record.getId(), List.of()).size());
            item.setScopeSummary(buildScopeSummary(scopeMap.get(record.getId())));
            result.add(item);
        }

        Page<ApprovalFlowPageVo> voPage = new Page<>(actualPageNumber, actualPageSize, entityPage.getTotalRow());
        voPage.setRecords(result);
        return voPage;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ApprovalFlowDetailVo getFlowDetail(BigInteger id) {
        ApprovalFlow flow = requireFlow(id);
        ApprovalFlowDetailVo detail = toDetailVo(flow);
        detail.setPendingInstanceCount(countPendingInstances(id));
        detail.setDeletable(detail.getPendingInstanceCount() == 0);
        detail.setScopes(loadScopeVos(id));
        detail.setSteps(loadStepVos(id));
        return detail;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BigInteger saveFlow(ApprovalFlowDetailVo request, BigInteger operatorId) {
        ApprovalFlowDetailVo normalized = normalizeRequest(request, true);
        Date now = new Date();
        ApprovalFlow flow = new ApprovalFlow();
        flow.setName(normalized.getName());
        flow.setResourceType(normalized.getResourceType());
        flow.setActionType(normalized.getActionType());
        flow.setPriority(normalized.getPriority());
        flow.setStatus(normalized.getStatus());
        flow.setVersion(1);
        flow.setRemark(normalized.getRemark());
        flow.setCreated(now);
        flow.setCreatedBy(operatorId);
        flow.setModified(now);
        flow.setModifiedBy(operatorId);
        approvalFlowMapper.insert(flow);
        replaceChildren(flow.getId(), normalized, operatorId, now);
        return flow.getId();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFlow(ApprovalFlowDetailVo request, BigInteger operatorId) {
        ApprovalFlow existing = requireFlow(request == null ? null : request.getId());
        ApprovalFlowDetailVo normalized = normalizeRequest(request, false);
        Date now = new Date();
        existing.setName(normalized.getName());
        existing.setResourceType(normalized.getResourceType());
        existing.setActionType(normalized.getActionType());
        existing.setPriority(normalized.getPriority());
        existing.setStatus(normalized.getStatus());
        existing.setVersion((existing.getVersion() == null ? 1 : existing.getVersion()) + 1);
        existing.setRemark(normalized.getRemark());
        existing.setModified(now);
        existing.setModifiedBy(operatorId);
        approvalFlowMapper.update(existing);
        clearChildren(existing.getId());
        replaceChildren(existing.getId(), normalized, operatorId, now);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void enableFlow(BigInteger id, BigInteger operatorId) {
        updateStatus(id, ApprovalFlowStatus.ENABLED.getCode(), operatorId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void disableFlow(BigInteger id, BigInteger operatorId) {
        updateStatus(id, ApprovalFlowStatus.DISABLED.getCode(), operatorId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeFlow(BigInteger id) {
        requireFlow(id);
        if (countPendingInstances(id) > 0) {
            throw new BusinessException("存在未完成审批实例，当前流程不允许删除");
        }
        clearChildren(id);
        approvalFlowMapper.deleteById(id);
    }

    private void updateStatus(BigInteger id, String status, BigInteger operatorId) {
        ApprovalFlow existing = requireFlow(id);
        if (ApprovalFlowStatus.ENABLED.getCode().equals(status)) {
            validateStoredStepAssignees(id);
        }
        existing.setStatus(status);
        existing.setModified(new Date());
        existing.setModifiedBy(operatorId);
        approvalFlowMapper.update(existing);
    }

    private ApprovalFlow requireFlow(BigInteger id) {
        if (id == null) {
            throw new BusinessException("流程ID不能为空");
        }
        ApprovalFlow flow = approvalFlowMapper.selectOneById(id);
        if (flow == null) {
            throw new BusinessException("审批流程不存在");
        }
        return flow;
    }

    private ApprovalFlowDetailVo normalizeRequest(ApprovalFlowDetailVo request, boolean create) {
        if (request == null) {
            throw new BusinessException("审批流程不能为空");
        }
        if (!create && request.getId() == null) {
            throw new BusinessException("流程ID不能为空");
        }
        if (!StringUtils.hasText(request.getName())) {
            throw new BusinessException("流程名称不能为空");
        }
        if (request.getPriority() == null) {
            throw new BusinessException("优先级不能为空");
        }
        ApprovalFlowDetailVo normalized = new ApprovalFlowDetailVo();
        normalized.setId(request.getId());
        normalized.setName(request.getName().trim());
        normalized.setResourceType(ApprovalResourceType.from(request.getResourceType()).getCode());
        normalized.setActionType(ApprovalActionType.from(request.getActionType()).getCode());
        normalized.setPriority(request.getPriority());
        normalized.setStatus(StringUtils.hasText(request.getStatus())
                ? ApprovalFlowStatus.from(request.getStatus()).getCode()
                : ApprovalFlowStatus.ENABLED.getCode());
        normalized.setRemark(request.getRemark());
        normalized.setScopes(normalizeScopes(request.getScopes()));
        normalized.setSteps(normalizeSteps(request.getSteps()));
        return normalized;
    }

    private List<ApprovalFlowScopeVo> normalizeScopes(List<ApprovalFlowScopeVo> scopes) {
        if (CollectionUtil.isEmpty(scopes)) {
            return new ArrayList<>();
        }
        Set<String> keys = new LinkedHashSet<>();
        List<ApprovalFlowScopeVo> result = new ArrayList<>();
        for (ApprovalFlowScopeVo item : scopes) {
            if (item == null || item.getScopeValue() == null || !StringUtils.hasText(item.getScopeType())) {
                continue;
            }
            ApprovalFlowScopeVo normalized = new ApprovalFlowScopeVo();
            normalized.setScopeType(ApprovalScopeType.from(item.getScopeType()).getCode());
            normalized.setScopeValue(item.getScopeValue());
            normalized.setIncludeChildren(item.getIncludeChildren() != null && item.getIncludeChildren() == 1 ? 1 : 0);
            String uniqueKey = normalized.getScopeType() + ":" + normalized.getScopeValue();
            if (keys.add(uniqueKey)) {
                result.add(normalized);
            }
        }
        return result;
    }

    private List<ApprovalFlowStepVo> normalizeSteps(List<ApprovalFlowStepVo> steps) {
        if (CollectionUtil.isEmpty(steps)) {
            throw new BusinessException("审批步骤不能为空");
        }
        List<ApprovalFlowStepVo> result = new ArrayList<>();
        int index = 1;
        for (ApprovalFlowStepVo item : steps) {
            if (item == null || !StringUtils.hasText(item.getStepName())) {
                continue;
            }
            ApprovalFlowStepVo normalized = new ApprovalFlowStepVo();
            normalized.setStepNo(index++);
            normalized.setStepName(item.getStepName().trim());
            normalized.setAssigneeType(item.getAssigneeType());
            normalized.setAssigneeTargetId(item.getAssigneeTargetId());
            approvalAssigneeService.normalizeStepAssignee(normalized);
            result.add(normalized);
        }
        if (result.isEmpty()) {
            throw new BusinessException("审批步骤不能为空");
        }
        return result;
    }

    private void clearChildren(BigInteger flowId) {
        approvalFlowScopeMapper.deleteByQuery(QueryWrapper.create().eq(ApprovalFlowScope::getFlowId, flowId));
        approvalFlowStepMapper.deleteByQuery(QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId));
    }

    private void replaceChildren(BigInteger flowId, ApprovalFlowDetailVo request, BigInteger operatorId, Date now) {
        for (ApprovalFlowScopeVo scopeVo : request.getScopes()) {
            ApprovalFlowScope scope = new ApprovalFlowScope();
            scope.setFlowId(flowId);
            scope.setScopeType(scopeVo.getScopeType());
            scope.setScopeValue(scopeVo.getScopeValue());
            scope.setIncludeChildren(scopeVo.getIncludeChildren());
            scope.setCreated(now);
            scope.setCreatedBy(operatorId);
            scope.setModified(now);
            scope.setModifiedBy(operatorId);
            approvalFlowScopeMapper.insert(scope);
        }
        for (ApprovalFlowStepVo stepVo : request.getSteps()) {
            ApprovalFlowStep step = new ApprovalFlowStep();
            step.setFlowId(flowId);
            step.setStepNo(stepVo.getStepNo());
            step.setStepName(stepVo.getStepName());
            step.setAssigneeType(stepVo.getAssigneeType());
            step.setAssigneeTargetId(stepVo.getAssigneeTargetId());
            step.setAssigneeTargetCode(stepVo.getAssigneeTargetCode());
            step.setAssigneeTargetName(stepVo.getAssigneeTargetName());
            step.setCreated(now);
            step.setCreatedBy(operatorId);
            step.setModified(now);
            step.setModifiedBy(operatorId);
            approvalFlowStepMapper.insert(step);
        }
    }

    private ApprovalFlowDetailVo toDetailVo(ApprovalFlow flow) {
        ApprovalFlowDetailVo detail = new ApprovalFlowDetailVo();
        detail.setId(flow.getId());
        detail.setName(flow.getName());
        detail.setResourceType(flow.getResourceType());
        detail.setActionType(flow.getActionType());
        detail.setPriority(flow.getPriority());
        detail.setStatus(flow.getStatus());
        detail.setVersion(flow.getVersion());
        detail.setRemark(flow.getRemark());
        detail.setCreated(flow.getCreated());
        detail.setModified(flow.getModified());
        return detail;
    }

    private List<ApprovalFlowScopeVo> loadScopeVos(BigInteger flowId) {
        return approvalFlowScopeMapper.selectListByQuery(QueryWrapper.create().eq(ApprovalFlowScope::getFlowId, flowId))
                .stream()
                .sorted(Comparator.comparing(ApprovalFlowScope::getScopeType).thenComparing(ApprovalFlowScope::getScopeValue))
                .map(item -> {
                    ApprovalFlowScopeVo scopeVo = new ApprovalFlowScopeVo();
                    scopeVo.setId(item.getId());
                    scopeVo.setScopeType(item.getScopeType());
                    scopeVo.setScopeValue(item.getScopeValue());
                    scopeVo.setIncludeChildren(item.getIncludeChildren());
                    return scopeVo;
                })
                .collect(Collectors.toList());
    }

    private List<ApprovalFlowStepVo> loadStepVos(BigInteger flowId) {
        return approvalFlowStepMapper.selectListByQuery(QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId))
                .stream()
                .sorted(Comparator.comparing(ApprovalFlowStep::getStepNo))
                .map(item -> {
                    ApprovalFlowStepVo stepVo = new ApprovalFlowStepVo();
                    stepVo.setId(item.getId());
                    stepVo.setStepNo(item.getStepNo());
                    stepVo.setStepName(item.getStepName());
                    stepVo.setAssigneeType(item.getAssigneeType());
                    stepVo.setAssigneeTargetId(item.getAssigneeTargetId());
                    stepVo.setAssigneeTargetCode(item.getAssigneeTargetCode());
                    stepVo.setAssigneeTargetName(item.getAssigneeTargetName());
                    return stepVo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 校验数据库中已保存步骤的审批对象仍然有效。
     *
     * @param flowId 流程ID
     */
    private void validateStoredStepAssignees(BigInteger flowId) {
        List<ApprovalFlowStepVo> steps = loadStepVos(flowId);
        if (CollectionUtil.isEmpty(steps)) {
            throw new BusinessException("审批步骤不能为空");
        }
        for (ApprovalFlowStepVo step : steps) {
            approvalAssigneeService.normalizeStepAssignee(step);
        }
    }

    private long countPendingInstances(BigInteger flowId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(ApprovalInstance::getFlowId, flowId)
                .in(ApprovalInstance::getStatus, List.of(
                        ApprovalInstanceStatus.PENDING.getCode(),
                        ApprovalInstanceStatus.PROCESSING.getCode()));
        return approvalInstanceMapper.selectCountByQuery(queryWrapper);
    }

    private Map<BigInteger, Long> loadPendingInstanceCountMap(List<BigInteger> flowIds) {
        if (CollectionUtil.isEmpty(flowIds)) {
            return Map.of();
        }
        QueryWrapper queryWrapper = QueryWrapper.create()
                .in(ApprovalInstance::getFlowId, flowIds)
                .in(ApprovalInstance::getStatus, List.of(
                        ApprovalInstanceStatus.PENDING.getCode(),
                        ApprovalInstanceStatus.PROCESSING.getCode()));
        List<ApprovalInstance> instances = approvalInstanceMapper.selectListByQuery(queryWrapper);
        return instances.stream().collect(Collectors.groupingBy(ApprovalInstance::getFlowId, Collectors.counting()));
    }

    private Map<BigInteger, List<ApprovalFlowScope>> loadScopeMap(List<BigInteger> flowIds) {
        if (CollectionUtil.isEmpty(flowIds)) {
            return Map.of();
        }
        List<ApprovalFlowScope> scopes = approvalFlowScopeMapper.selectListByQuery(
                QueryWrapper.create().in(ApprovalFlowScope::getFlowId, flowIds));
        return scopes.stream().collect(Collectors.groupingBy(
                ApprovalFlowScope::getFlowId, LinkedHashMap::new, Collectors.toList()));
    }

    private Map<BigInteger, List<ApprovalFlowStep>> loadStepMap(List<BigInteger> flowIds) {
        if (CollectionUtil.isEmpty(flowIds)) {
            return Map.of();
        }
        List<ApprovalFlowStep> steps = approvalFlowStepMapper.selectListByQuery(
                QueryWrapper.create().in(ApprovalFlowStep::getFlowId, flowIds));
        return steps.stream().collect(Collectors.groupingBy(
                ApprovalFlowStep::getFlowId, LinkedHashMap::new, Collectors.toList()));
    }

    private String buildScopeSummary(List<ApprovalFlowScope> scopes) {
        if (CollectionUtil.isEmpty(scopes)) {
            return "全部";
        }
        long categoryCount = scopes.stream()
                .filter(item -> ApprovalScopeType.CATEGORY.getCode().equals(item.getScopeType()))
                .count();
        long deptCount = scopes.stream()
                .filter(item -> ApprovalScopeType.DEPT.getCode().equals(item.getScopeType()))
                .count();
        List<String> parts = new ArrayList<>();
        if (categoryCount > 0) {
            parts.add("分类 " + categoryCount);
        }
        if (deptCount > 0) {
            parts.add("部门 " + deptCount);
        }
        return String.join(" / ", parts);
    }
}
