package cn.axzo.workflow.server.controller.delegate;

import cn.axzo.karma.client.model.request.ListFlowTaskAssignerReq;
import cn.axzo.karma.client.model.response.FlowTaskAssignerResp;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverScopeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.deletage.approverscope.ApproverScopeDTO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_WORKSPACE_TYPE;

/**
 * 基于"转交给管理员"查询审批人
 *
 * @author wangli
 * @since 2023/11/16 11:44
 */
@Slf4j
@Component
public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector {

    @Resource
    private SupportRefreshProperties supportRefreshProperties;

    @Override
    public String getType() {
        return ApproverEmptyHandleTypeEnum.transferToAdmin.getType();
    }

    @Override
    public List<BpmnTaskDelegateAssigner> select(FlowElement flowElement, DelegateExecution execution,
                                                 Boolean throwException) {
        return super.select(flowElement, execution, throwException);
    }

    @Override
    protected List<BpmnTaskDelegateAssigner> invokeService(FlowElement flowElement, DelegateExecution execution,
                                                           ApproverScopeDTO scopeDto) {

        if (Boolean.TRUE.equals(supportRefreshProperties.getUseNewToAdminApi())) {
            return invokeNewQuery(flowElement, execution, scopeDto);
        } else {
            return invokeOldQuery(flowElement, execution, scopeDto);
        }

    }

    private List<BpmnTaskDelegateAssigner> invokeOldQuery(FlowElement flowElement, DelegateExecution execution, ApproverScopeDTO scopeDto) {
        ListFlowTaskAssignerReq.ListFlowTaskAssignerReqBuilder builder = ListFlowTaskAssignerReq.builder();
        if (!CollectionUtils.isEmpty(scopeDto.getOrgScopes())) {
            builder.orgScopes(ListUtils.emptyIfNull(scopeDto.getOrgScopes()).stream()
                    .map(e -> BeanUtil.copyProperties(e, ListFlowTaskAssignerReq.OrgScope.class))
                    .collect(Collectors.toList()));
        }
        if (!CollectionUtils.isEmpty(scopeDto.getWorkerTeamScopes())) {
            builder.workerTeamScopes(ListUtils.emptyIfNull(scopeDto.getOrgScopes()).stream()
                    .map(w -> BeanUtil.copyProperties(w, ListFlowTaskAssignerReq.OrgScope.class))
                    .collect(Collectors.toList()));
        }
        ListFlowTaskAssignerReq req = builder
                .procInstId(execution.getProcessInstanceId())
                .workspaceAdmin(false)
                .build();
        BpmnMetaParserHelper.getApproverScope((UserTask) flowElement)
                .ifPresent(i -> {
                    if (Objects.equals(i, ApproverScopeEnum.projectWorkspace)) {
                        req.setWorkspaceAdmin(true);
                    }
                });
        List<FlowTaskAssignerResp> flowTaskAssigners =
                parseApiResult(() -> flowSupportApi.listTaskAssignerAdmin(req),
                        "审批节点: " + flowElement.getId() + ", 通过管理员查询审批人",
                        "cn.axzo.karma.client.feign.FlowSupportApi.listTaskAssignerAdmin", req);


        if (CollUtil.isEmpty(flowTaskAssigners)) {
            return super.invokeService(flowElement, execution, scopeDto);
        }
        return BeanUtil.copyToList(flowTaskAssigners, BpmnTaskDelegateAssigner.class);
    }

    private List<BpmnTaskDelegateAssigner> invokeNewQuery(FlowElement flowElement, DelegateExecution execution, ApproverScopeDTO scopeDto) {
        Optional<ApproverScopeEnum> approverScope = BpmnMetaParserHelper.getApproverScope((UserTask) flowElement);
        Optional<ApproverSpecifyEnum> optSpecify = BpmnMetaParserHelper.getApproverSpecify((UserTask) flowElement);
        // 如果是项目部，且审批人指定的配法不是岗位或角色，则默认直接返回空集合，走转交管理员后为空的最终兜底逻辑
        if (optSpecify.isPresent() && approverScope.isPresent()
                && Objects.equals(ApproverScopeEnum.projectWorkspace, approverScope.get())
                && !Objects.equals(ApproverSpecifyEnum.position, optSpecify.get())
                && !Objects.equals(ApproverSpecifyEnum.role, optSpecify.get())) {
            return Collections.emptyList();
        }

        ListFlowTaskAssignerReq.ListFlowTaskAssignerReqBuilder builder = ListFlowTaskAssignerReq.builder();
        if (!CollectionUtils.isEmpty(scopeDto.getOrgScopes())) {
            builder.orgScopes(ListUtils.emptyIfNull(scopeDto.getOrgScopes()).stream()
                    .map(e -> BeanUtil.copyProperties(e, ListFlowTaskAssignerReq.OrgScope.class))
                    .collect(Collectors.toList()));
        }
        if (!CollectionUtils.isEmpty(scopeDto.getWorkerTeamScopes())) {
            builder.workerTeamScopes(ListUtils.emptyIfNull(scopeDto.getOrgScopes()).stream()
                    .map(w -> BeanUtil.copyProperties(w, ListFlowTaskAssignerReq.OrgScope.class))
                    .collect(Collectors.toList()));
        }
        ListFlowTaskAssignerReq req = builder.workspaceAdmin(false).build();
        req.setProcInstId(execution.getProcessInstanceId());
        if (approverScope.isPresent() && Objects.equals(ApproverScopeEnum.entWorkspace, approverScope.get()) &&
                optSpecify.isPresent() && Objects.equals(ApproverSpecifyEnum.fixedPerson, optSpecify.get())) {
            req.setCooperateTypes(Sets.newHashSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 30));
        } else {
            req.setCooperateTypes(getCooperationTypes(flowElement));
        }
        // 发起人主管找其超管时，需要将发起人的数据包装进 orgScope
        if (optSpecify.isPresent() && Objects.equals(ApproverSpecifyEnum.initiatorLeader, optSpecify.get())) {
            BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(execution.getVariable(INTERNAL_INITIATOR, String.class));
            Integer workspaceType = execution.getVariable(INTERNAL_PROCESS_WORKSPACE_TYPE, Integer.class);
            req.setOrgScopes(Lists.newArrayList(new ListFlowTaskAssignerReq.OrgScope(workspaceType, Long.parseLong(initiator.getTenantId()), Long.parseLong(initiator.getOuId()), null)));
        }
        approverScope.ifPresent(i -> {
            if (Objects.equals(i, ApproverScopeEnum.projectWorkspace)) {
                req.setWorkspaceAdmin(true);
            }
        });
        boolean flag = false;
        if (CollectionUtils.isEmpty(req.getCooperateTypes())) {
            flag = true;
        }
        List<FlowTaskAssignerResp> flowTaskAssigners =
                parseApiResult(() -> flowSupportApi.listTaskAssignerAdminV2(req),
                        "审批节点: " + flowElement.getId() + ", 通过管理员查询审批人" + (flag ? "需要重新发布选择节点配置" : ""),
                        "cn.axzo.karma.client.feign.FlowSupportApi#listTaskAssignerAdminV2",
                        req);

        if (CollUtil.isEmpty(flowTaskAssigners)) {
            return super.invokeService(flowElement, execution, scopeDto);
        }
        return BeanUtil.copyToList(flowTaskAssigners, BpmnTaskDelegateAssigner.class);
    }

    public Set<Integer> getCooperationTypes(FlowElement flowElement) {
        return BpmnMetaParserHelper.getApproverSpecifyValue((UserTask) flowElement)
                .map(value -> JSON.parseArray(value, String.class)
                        .stream().map(JSON::parseObject)
                        .map(i -> i.getString("type"))
                        .filter(StringUtils::hasText)
                        .collect(Collectors.toList())
                        .stream()
                        .flatMap(s -> Arrays.stream(s.split(",")))
                        .map(Integer::parseInt)
                        .collect(Collectors.toSet())
                )
                .orElse(Collections.emptySet());
    }

}
