/*
 * Copyright (c) 2023 codvision.com All Rights Reserved.
 */

package com.codvision.workflow.service.impl;

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.codvision.auth.entity.Org;
import com.codvision.auth.entity.SysUser;
import com.codvision.auth.service.IOrgService;
import com.codvision.auth.service.ISysUserService;
import com.codvision.authcore.bean.SysUserBean;
import com.codvision.authcore.bean.UserAssociatedInfoBean;
import com.codvision.authcore.util.AuthUtil;
import com.codvision.commoncore.utils.Preconditions;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.service.impl.BaseServiceImpl;
import com.codvision.webcore.util.QueryPageUtil;
import com.codvision.workflow.entity.WorkFlowData;
import com.codvision.workflow.entity.vo.WorkFlowDataVO;
import com.codvision.workflow.mapper.WorkFlowDataMapper;
import com.codvision.workflow.service.WorkFlowDataService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.HistoryService;
import org.flowable.identitylink.api.IdentityLinkInfo;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import static com.codvision.base.support.BaseConst.COMMA_SYMBOL;

/**
 * @author hxl
 */
@Service
@RequiredArgsConstructor
public class WorkFlowDataServiceImpl extends BaseServiceImpl<WorkFlowDataMapper, WorkFlowData, WorkFlowDataVO> implements WorkFlowDataService {

    private final HistoryService historyService;
    private final ISysUserService sysUserService;
    private final IOrgService orgService;

    @Override
    public WorkFlowData getById(Serializable id) {
        Preconditions.checkArgument(id != null, 30002);
        return super.getById(id);
    }

    @Override
    public IPage<WorkFlowDataVO> getPageResult(QueryPage<Map<String, Object>> queryPage) {
        SysUserBean currentUser = AuthUtil.getCurrentUser();
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        paraMap.put("uid", currentUser.getId());
        paraMap.put("oid", currentUser.getActiveOid());
        IPage<WorkFlowDataVO> pageResult = getBaseMapper().getPageResult(queryPage.getPage(), paraMap);
        pageResult.getRecords().forEach(this::setCurrentTaskInfo);
        return pageResult;
    }

    @Override
    public IPage<WorkFlowDataVO> myInitiatedList(QueryPage<Map<String, Object>> queryPage) {
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        paraMap.put("uid", AuthUtil.getCurrentUser().getId());
        String statusArray = (String) paraMap.get("statusArray");
        if (StringUtils.isNotBlank(statusArray)) {
            paraMap.put("statusList", Arrays.stream(statusArray.split(COMMA_SYMBOL)).map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList()));
        }
        return getBaseMapper().myInitiatedList(queryPage.getPage(), paraMap);
    }

    @Override
    public IPage<WorkFlowDataVO> todoList(QueryPage<Map<String, Object>> queryPage) {
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        Boolean isContainClaim = MapUtil.getBool(paraMap, "isContainClaim");
        if (isContainClaim == null) {
            isContainClaim = true;
            paraMap.put("isContainClaim", true);
        }
        initMap(paraMap, isContainClaim);
        return getBaseMapper().todoList(queryPage.getPage(), paraMap);
    }

    @Override
    public IPage<WorkFlowDataVO> finishedList(QueryPage<Map<String, Object>> queryPage) {
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        paraMap.put("uid", AuthUtil.getCurrentUser().getId());
        return getBaseMapper().finishedList(queryPage.getPage(), paraMap);
    }

    @Override
    public IPage<WorkFlowDataVO> claimList(QueryPage<Map<String, Object>> queryPage) {
        Map<String, Object> paraMap = QueryPageUtil.queryMap(queryPage);
        initMap(paraMap, true);
        return getBaseMapper().claimList(queryPage.getPage(), paraMap);
    }

    @Override
    public void updateFormDataByInstanceId(String instanceId, String jsonData) {
        getBaseMapper().updateFormDataByInstanceId(instanceId, jsonData);
    }

    @Override
    public WorkFlowData getFormDataByInstanceId(String instanceId) {
        return this.getOne(Wrappers.<WorkFlowData>lambdaQuery().eq(WorkFlowData::getInstanceId, instanceId));
    }

    @Override
    public int getCountByDeployId(String deployId) {
        return (int) this.count(Wrappers.<WorkFlowData>lambdaQuery().eq(WorkFlowData::getDeployId, deployId));
    }

    @Override
    public void delAllByDeployId(String deployId) {
        this.remove(Wrappers.<WorkFlowData>lambdaQuery().eq(WorkFlowData::getDeployId, deployId));
    }

    @Override
    public void delByInstanceId(String instanceId) {
        this.remove(Wrappers.<WorkFlowData>lambdaQuery().eq(WorkFlowData::getInstanceId, instanceId));
    }


    private void initMap(Map<String, Object> paraMap, boolean isContainClaim) {
        SysUserBean currentUser = AuthUtil.getCurrentUser();
        paraMap.put("uid", currentUser.getId().toString());
        paraMap.put("roleId", currentUser.getActiveRoleId());
        if (isContainClaim) {
            paraMap.put("groupIds", currentUser.getAssociatedInfos().stream().map(UserAssociatedInfoBean::getOid).map(String::valueOf).collect(Collectors.toList()));
        }
    }

    private void setCurrentTaskInfo(WorkFlowDataVO workFlowDataVO) {
        // 获取历史任务信息
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(workFlowDataVO.getInstanceId())
                .orderByHistoricTaskInstanceStartTime().desc().includeIdentityLinks().list();
        if (CollectionUtils.isEmpty(historicTaskInstances)) {
            return;
        }
        // 获取当前任务信息
        HistoricTaskInstance historicTaskInstance = historicTaskInstances.get(0);
        // 设置当前任务名称
        workFlowDataVO.setTaskName(historicTaskInstance.getName());
        workFlowDataVO.setTaskId(historicTaskInstance.getId());
        if (StringUtils.isNotBlank(historicTaskInstance.getAssignee())) {
            // 获取办理人信息
            SysUser sysUser = sysUserService.getById(Integer.valueOf(historicTaskInstance.getAssignee()));
            if (sysUser != null) {
                workFlowDataVO.setAssigneeOrOrgName(sysUser.getName());
            }
        } else if (CollectionUtils.isNotEmpty(historicTaskInstance.getIdentityLinks())) {
            // 获取候选人或者候选组信息
            List<? extends IdentityLinkInfo> identityLinks = historicTaskInstance.getIdentityLinks().stream()
                    .filter(identityLink -> IdentityLinkType.CANDIDATE.equals(identityLink.getType())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(identityLinks)) {
                return ;
            }

            Optional<String> optional = identityLinks.stream().map(IdentityLinkInfo::getGroupId).filter(StringUtils::isNotBlank).findFirst();
            if (optional.isPresent()) {
                Org org = orgService.getById(Integer.valueOf(optional.get()));
                if (org != null) {
                    workFlowDataVO.setAssigneeOrOrgName(org.getName());
                    return ;
                }
            }

            List<String> uidList = identityLinks.stream().map(IdentityLinkInfo::getUserId).filter(StringUtils::isNotBlank).collect(Collectors.toList());
            List<String> names = new ArrayList<>();
            for (String uid : uidList) {
                SysUser sysUser = sysUserService.getById(Integer.valueOf(uid));
                if (sysUser == null || StringUtils.isBlank(sysUser.getName())) {
                    continue;
                }
                names.add(sysUser.getName());
            }

            workFlowDataVO.setAssigneeOrOrgName(String.join(",", names));
        }
    }
}
