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

import cn.axzo.orggateway.api.nodeuser.OrgNodeUserApi;
import cn.axzo.orggateway.api.nodeuser.dto.FlowTaskAssignerScopeDTO;
import cn.axzo.orggateway.api.nodeuser.enums.FlowTaskAssignerQuerySceneEnum;
import cn.axzo.orggateway.api.nodeuser.req.FlowTaskAssignerV2Req;
import cn.axzo.orggateway.api.nodeuser.resp.FlowTaskAssignerV2Resp;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyRangeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyRangeUnitEnum;
import cn.axzo.workflow.common.enums.CooperateShipTypeEnum;
import cn.axzo.workflow.common.enums.SignApproverOrgLimitEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.CooperationOrgDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import com.alibaba.fastjson.JSON;
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.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
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.code.FlowableEngineRespCode.ENGINE_POSITION_V2_CONFIG_INVALID;
import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.CLOSE_PROCESS_ASSIGNER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum.transferToAdmin;
import static cn.axzo.workflow.common.enums.ApproverSpecifyRangeUnitEnum.in_project;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverEmptyHandleType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecifyRange;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecifyRangeOrgLimit;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecifyRangeUnit;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecifyValueV2;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getAreaFilterEnable;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCooperateShipType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getSpecialtyFilterEnable;

/**
 * 基于新版配置的岗位查询审批人
 *
 * @author wangli
 * @since 2025-08-15 10:58
 */
@Slf4j
@Component
public class BasedPositionV2TaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector {
    @Resource
    private OrgNodeUserApi orgNodeUserApi;

    @Override
    public String getType() {
        return ApproverSpecifyEnum.position_v2.getType();
    }

    @Override
    public List<BpmnTaskDelegateAssigner> select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) {
        Optional<ApproverSpecifyRangeEnum> optRange = getApproverSpecifyRange(flowElement);
        if (!optRange.isPresent()) {
            throw new WorkflowEngineException(ENGINE_POSITION_V2_CONFIG_INVALID, "级联配置", flowElement.getId());
        }

        CooperationOrgDTO orgDTO = (CooperationOrgDTO) execution.getVariables().get(BIZ_ORG_RELATION);

        // 获取发起人
        BpmnTaskDelegateAssigner initiator =
                BpmnTaskDelegateAssigner.toObjectCompatible(execution.getVariables().get(INTERNAL_INITIATOR));

        FlowTaskAssignerV2Req.FlowTaskAssignerV2ReqBuilder v2ReqBuilder = FlowTaskAssignerV2Req.builder()
                .scene(FlowTaskAssignerQuerySceneEnum.BY_JOB)
                .jobCodes(getApproverSpecifyValue(flowElement))
                .initiatorPersonId(initiator.parsePersonId())
                .areaCodes(getAreaFilterEnable(flowElement) ? Sets.newHashSet(ListUtils.emptyIfNull(orgDTO.getIncludeAreaCodes())) : Sets.newHashSet())
                .specialtyCodes(getSpecialtyFilterEnable(flowElement) ? Sets.newHashSet(ListUtils.emptyIfNull(orgDTO.getIncludeSpecialtyCodes())) : Sets.newHashSet())
                .querySupervisorWhileMissMatched(getApproverEmptyHandleType(flowElement).filter(type -> Objects.equals(type, transferToAdmin)).isPresent());
        switch (optRange.get()) {
            case within_the_project:
                log.info("executing position v2 with within_the_project");
                v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
                        .nodeId(orgDTO.getNodeId())
                        .upLevel(SignApproverOrgLimitEnum.LV_ALL.getCode())
                        .crossDomain(false)
                        .build());
                break;
            case within_the_project_construction_units:
                log.info("executing position v2 with within_the_project_construction_units");
                CooperateShipTypeEnum workflowType = getCooperateShipType(flowElement).orElseThrow(() -> new WorkflowEngineException(ENGINE_POSITION_V2_CONFIG_INVALID, "缺少参建单位类型配置", flowElement.getId()));

                v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
                        .nodeId(orgDTO.getNodeId())
                        .upLevel(SignApproverOrgLimitEnum.LV_ALL.getCode())
                        .crossDomain(true)
                        .cooperateTypes(Sets.newHashSet(cn.axzo.orgmanax.dto.cooperateship.enums.CooperateShipTypeEnum.valueOf(workflowType.name())))
                        .build());
                break;
            case specified_org:
                log.info("executing position v2 with specified_org");
                Optional<ApproverSpecifyRangeUnitEnum> approverSpecifyRangeUnit = getApproverSpecifyRangeUnit(flowElement);

                v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
                        .nodeId(orgDTO.getNodeId())
                        .upLevel(getApproverSpecifyRangeOrgLimit(flowElement).orElse(SignApproverOrgLimitEnum.UNKNOWN).getCode())
                        .crossDomain(approverSpecifyRangeUnit.filter(unit -> unit != in_project).isPresent())
                        .entUpLevel(approverSpecifyRangeUnit.orElse(ApproverSpecifyRangeUnitEnum.UNKNOWN).getValue())
                        .build());
                break;
            case initiator:
                log.info("executing position v2 with initiator");
                Optional<ApproverSpecifyRangeUnitEnum> approverSpecifyRangeUnit4Initiator = getApproverSpecifyRangeUnit(flowElement);
                v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
                        .nodeId(initiator.parseNodeId())
                        .upLevel(getApproverSpecifyRangeOrgLimit(flowElement).orElse(SignApproverOrgLimitEnum.UNKNOWN).getCode())
                        .crossDomain(approverSpecifyRangeUnit4Initiator.filter(unit -> unit != in_project).isPresent())
                        .entUpLevel(approverSpecifyRangeUnit4Initiator.orElse(ApproverSpecifyRangeUnitEnum.UNKNOWN).getValue())
                        .build());
                break;
            case pre_node_approver:
                log.info("executing position v2 with pre_node_approver");
                Optional<ApproverSpecifyRangeUnitEnum> approverSpecifyRangeUnit4PreNodeApprover = getApproverSpecifyRangeUnit(flowElement);
                BpmnTaskDelegateAssigner lastOperationApprover =
                        BpmnTaskDelegateAssigner.toObjectCompatible(execution.getVariables().get(CLOSE_PROCESS_ASSIGNER));
                if (Objects.isNull(lastOperationApprover)) {
                    return Collections.emptyList();
                }
                v2ReqBuilder.scope(FlowTaskAssignerScopeDTO.builder()
                        .nodeId(lastOperationApprover.parseNodeId())
                        .upLevel(getApproverSpecifyRangeOrgLimit(flowElement).orElse(SignApproverOrgLimitEnum.UNKNOWN).getCode())
                        .crossDomain(approverSpecifyRangeUnit4PreNodeApprover.filter(unit -> unit != in_project).isPresent())
                        .entUpLevel(approverSpecifyRangeUnit4PreNodeApprover.orElse(ApproverSpecifyRangeUnitEnum.UNKNOWN).getValue())
                        .build());
                break;
            default:
                break;
        }
        FlowTaskAssignerV2Req request = v2ReqBuilder.build();
        List<FlowTaskAssignerV2Resp> apiResultUsers = parseFoundationApiResult(() -> orgNodeUserApi.listFlowTaskAssignerV2(request), "新版查询岗位下的人" + execution.getProcessInstanceId() + flowElement.getId(),
                "cn.axzo.orggateway.api.nodeuser.OrgNodeUserApi.listFlowTaskAssignerV2", request);
        return convertApprover(apiResultUsers);
    }

    public Set<String> getApproverSpecifyValue(FlowElement flowElement) {
        return getApproverSpecifyValueV2(flowElement)
                .map(value -> JSON.parseArray(value, String.class).stream().map(JSON::parseObject)
                        .map(i -> i.getString("value"))
                        .collect(Collectors.toSet()))
                .orElse(Collections.emptySet());
    }
}
