package com.bcxin.tenant.open.domains.services.impls;

import com.bcxin.tenant.open.document.domains.documents.*;
import com.bcxin.tenant.open.document.domains.documents.messages.RollCallMessageContentDocument;
import com.bcxin.tenant.open.document.domains.repositories.InstantActivityDataDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.NotifyMessageDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.RdEmployeeDocumentRepository;
import com.bcxin.tenant.open.document.domains.repositories.RollCallProgressStatusDocumentRepository;
import com.bcxin.tenant.open.document.domains.utils.DocumentScopeFilterUtils;
import com.bcxin.tenant.open.domains.BillPaymentRuleConfig;
import com.bcxin.tenant.open.domains.entities.RollCallEmployeeEntity;
import com.bcxin.tenant.open.domains.entities.RollCallEmployeeHistoryEntity;
import com.bcxin.tenant.open.domains.entities.RollCallEntity;
import com.bcxin.tenant.open.domains.entities.TenantUserView;
import com.bcxin.tenant.open.domains.events.BatchDispatchRollCallEmployeeEvent;
import com.bcxin.tenant.open.domains.events.ReplyRollCallEmployeeStatusEvent;
import com.bcxin.tenant.open.domains.exceptions.GeTuiBadTenantException;
import com.bcxin.tenant.open.domains.pojo.OrganizationPoJo;
import com.bcxin.tenant.open.domains.pojo.RollCallRulePoJo;
import com.bcxin.tenant.open.domains.pojo.SecurityDepartPoJo;
import com.bcxin.tenant.open.domains.pojo.SecurityStationPoJo;
import com.bcxin.tenant.open.domains.repositories.RollCallEmployeeHistoryRepository;
import com.bcxin.tenant.open.domains.repositories.RollCallEmployeeRepository;
import com.bcxin.tenant.open.domains.repositories.RollCallRepository;
import com.bcxin.tenant.open.domains.repositories.TenantUserViewRepository;
import com.bcxin.tenant.open.domains.services.RollCallService;
import com.bcxin.tenant.open.domains.services.commands.*;
import com.bcxin.tenant.open.domains.services.commands.results.CancelRollCallEmployeeCommandResult;
import com.bcxin.tenant.open.domains.services.commands.results.CreateRollCallCommandResult;
import com.bcxin.tenant.open.infrastructure.redis.RedisConfig;
import com.bcxin.tenant.open.infrastructures.UnitWork;
import com.bcxin.tenant.open.infrastructures.components.IdWorker;
import com.bcxin.tenant.open.infrastructures.components.JsonProvider;
import com.bcxin.tenant.open.infrastructures.constants.KafkaConstants;
import com.bcxin.tenant.open.infrastructures.enums.NotifyMessageType;
import com.bcxin.tenant.open.infrastructures.enums.RollCallStatus;
import com.bcxin.tenant.open.infrastructures.enums.RollCallType;
import com.bcxin.tenant.open.infrastructures.events.EventDispatcher;
import com.bcxin.tenant.open.infrastructures.exceptions.*;
import com.bcxin.tenant.open.infrastructures.utils.BusinessUtil;
import com.bcxin.tenant.open.infrastructures.utils.ExceptionUtil;
import com.bcxin.tenant.open.infrastructures.utils.StringUtil;
import com.redis.om.spring.search.stream.EntityStream;
import com.redis.om.spring.search.stream.SearchStream;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

@Service
public class RollCallServiceImpl implements RollCallService {
    private static final Logger logger = LoggerFactory.getLogger(RollCallServiceImpl.class);

    private final UnitWork unitWork;
    private final RollCallRepository rollCallRepository;
    private final RollCallEmployeeRepository rollCallEmployeeRepository;
    private final RollCallEmployeeHistoryRepository rollCallEmployeeHistoryRepository;
    private final KafkaTemplate kafkaTemplate;
    private final JsonProvider jsonProvider;

    private final EntityStream entityStream;

    private final IdWorker idWorker;
    private final EventDispatcher eventDispatcher;
    private final TenantUserViewRepository tenantUserViewRepository;
    private final NotifyMessageDocumentRepository notifyMessageDocumentRepository;
    private final InstantActivityDataDocumentRepository instantActivityDataDocumentRepository;
    private final RollCallProgressStatusDocumentRepository rollCallProgressStatusDocumentRepository;
    private final BillPaymentRuleConfig billPaymentRuleConfig;

    public RollCallServiceImpl(UnitWork unitWork, RollCallRepository rollCallRepository,
                               RollCallEmployeeRepository rollCallEmployeeRepository,
                               RollCallEmployeeHistoryRepository rollCallEmployeeHistoryRepository,
                               KafkaTemplate kafkaTemplate,
                               JsonProvider jsonProvider,
                               EntityStream entityStream, IdWorker idWorker,
                               EventDispatcher eventDispatcher,
                               TenantUserViewRepository tenantUserViewRepository,
                               NotifyMessageDocumentRepository notifyMessageDocumentRepository,
                               InstantActivityDataDocumentRepository instantActivityDataDocumentRepository,
                               RollCallProgressStatusDocumentRepository rollCallProgressStatusDocumentRepository, BillPaymentRuleConfig billPaymentRuleConfig) {
        this.unitWork = unitWork;
        this.rollCallRepository = rollCallRepository;
        this.rollCallEmployeeRepository = rollCallEmployeeRepository;
        this.rollCallEmployeeHistoryRepository = rollCallEmployeeHistoryRepository;
        this.kafkaTemplate = kafkaTemplate;
        this.jsonProvider = jsonProvider;
        this.entityStream = entityStream;
        this.idWorker = idWorker;
        this.eventDispatcher = eventDispatcher;
        this.tenantUserViewRepository = tenantUserViewRepository;
        this.notifyMessageDocumentRepository = notifyMessageDocumentRepository;
        this.instantActivityDataDocumentRepository = instantActivityDataDocumentRepository;
        this.rollCallProgressStatusDocumentRepository = rollCallProgressStatusDocumentRepository;
        this.billPaymentRuleConfig = billPaymentRuleConfig;
    }

    @Override
    public CreateRollCallCommandResult dispatch(CreateRollCallCommand command) {
        if (command.getOperator() == null) {
            throw new BadTenantException("操作点名的人员不能为空");
        }

        if (CollectionUtils.isEmpty(command.getSuperviseDepartIds()) &&
                !StringUtils.hasLength(command.getOperator().getActionOrganizationId())) {
            throw new BadTenantException("监管和企业信息不能同时为空");
        }

        List<RdEmployeeDocument> employeeDocuments = getMatchEmployees(command);

        if (CollectionUtils.isEmpty(employeeDocuments)) {
            throw new NoFoundTenantException("无需点名, 找不到人员数据");
        }

        if (employeeDocuments.size() > 200) {
            logger.error("非预期数量(size={}); 演示过程中, 不允许一次性点名超过200个人", employeeDocuments.size());
            throw new NoAllowedTenantException("非预期数量; 演示过程中, 不允许一次性点名超过200个人");
        }

        Collection<String> stationIds = command.getSecurityStationIds();
        if (CollectionUtils.isEmpty(stationIds)) {
            stationIds = new HashSet<>();
        }

        String tranId = this.unitWork.beginTransaction();
        RollCallEntity rollCall =
                RollCallEntity.create(
                        this.jsonProvider,
                        command.getOperator().getActionOrganizationId(),
                        command.getOperator().getActionOrganizationName(),
                        command.getOperator().getActionEmployeeId(),
                        command.getOperator().getActionEmployeeName(),
                        employeeDocuments.size(),
                        idWorker.getNextId(),
                        RollCallRulePoJo.create(command.getRollCallPlanId(), command.getTimeValue()),
                        command.getSuperviseDepartIds(),
                        stationIds,
                        command.getStationTypes()
                );

        try {
            this.rollCallRepository.insert(rollCall);
            /***判断目前区域是都是调度者支付账单费用*/
            Optional<RdEmployeeDocument> operatorDocOpt = this.entityStream.of(RdEmployeeDocument.class)
                    .filter(RdEmployeeDocument$.ID.eq(command.getOperator().getActionEmployeeId())).findFirst();
            if(!operatorDocOpt.isPresent()){
                throw new BadTenantException("操作点名的人员在系统中不存在");
            }
            RdEmployeeDocument operatorDoc = operatorDocOpt.get();
            boolean isOperatorThePaymentOwner = billPaymentRuleConfig.isPaymentOwner(
                    rollCall.getActionOrganizationId(),
                    operatorDoc.getInstitutional(),
                    operatorDoc.getSuperviseRegionCode()
            );
            List<ProducerRecord> records = new ArrayList<>();
            List<RollCallEmployeeEntity> employees = new ArrayList<>();

            for (int index = 0; index < employeeDocuments.size(); index++) {
                RdEmployeeDocument ii = employeeDocuments.get(index);
                String childId
                        = String.format("%s-%s", rollCall.getId(), StringUtil.leftPad(String.valueOf(index + 1), 6));
                RollCallEmployeeEntity employeeEntity
                        = RollCallEmployeeEntity.create(
                        childId,
                        jsonProvider,
                        rollCall,
                        ii.getTenantEmployeeId(),
                        ii.getTenantUserId(),
                        ii.getTenantImUserId(),
                        ii.getName(),
                        SecurityDepartPoJo.create(
                                ii.getSuperviseDepartId(), ii.getSuperviseDepartName(),
                                command.getOperator().getActionEmployeeId(),
                                command.getOperator().getActionEmployeeName()),
                        SecurityStationPoJo.create(
                                ii.getSecurityStationId(), ii.getSecurityStationName(),
                                BusinessUtil.isStationResponsible(ii.getSelectedResponsibleOfStationId(ii.getSecurityStationId())),
                                command.getStationTypes()),
                        OrganizationPoJo.create(ii.getOrganizationId(), ii.getCompanyName()),
                        isOperatorThePaymentOwner ? rollCall.getActionOrganizationId() : ii.getOrganizationId()
                );

                employees.add(employeeEntity);
                records.add(new ProducerRecord<>(
                        KafkaConstants.TOPIC_DISPATCH_ROLL_CALL_RECORD,
                        employeeEntity.getId(), this.jsonProvider.getJson(employeeEntity)
                ));
            }

            this.rollCallEmployeeRepository.batchInsert(employees);

            this.kafkaTemplate.execute(producer -> {
                try {
                    for (int index = 0; index < records.size(); index++) {
                        ProducerRecord record = records.get(index);
                        producer.send(record);
                    }
                } catch (Exception ex) {
                    throw new BadTenantException("推送数据到Kafka发生异常", ex);
                }

                return true;
            });

            /**
             * 仅人工点名的时候才会生成这个记录
             */
            if (command.getRollCallPlanId() == null) {
                this.rollCallProgressStatusDocumentRepository.save(
                        RollCallProgressStatusDocument.create(
                                rollCall.getId(), employeeDocuments.size()
                        )
                );
            }

            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);

            throw new BadTenantException("提交点名信息发生异常", ex);
        }

        return CreateRollCallCommandResult.create(rollCall.getId(), rollCall.getCountOfRollCallPerson());
    }

    @Override
    public void dispatch(BatchRollCallEmployeeCommand command) {
        if (CollectionUtils.isEmpty(command.getData())) {
            return;
        }

        Collection<String> allRollCallEmployeeIds =
                command.getData().stream().map(ii -> {
                    return this.jsonProvider.toObject(RollCallEmployeeEntity.class, ii);
                }).map(ii -> ii.getId()).distinct().collect(Collectors.toList());
        int retryIndex = 0;

        while (allRollCallEmployeeIds.size() > 0) {
            Collection<RollCallEmployeeEntity> callEmployees =
                    this.rollCallEmployeeRepository.findByIds(allRollCallEmployeeIds);
            if (!CollectionUtils.isEmpty(callEmployees)) {
                StringBuilder error = new StringBuilder();
                String tranId = this.unitWork.beginTransaction();

                try {
                    Collection<String> tenantUserIds = callEmployees.stream()
                            .map(ii -> ii.getCalledTenantUserId()).collect(Collectors.toSet());
                    Collection<TenantUserView> selectedTenantUsers =
                            this.tenantUserViewRepository.getAllByIds(tenantUserIds);

                    Collection<NotifyMessageDocument> messageDocuments =
                            callEmployees.stream().map(ii -> {
                                return NotifyMessageDocument.create(
                                        jsonProvider,
                                        NotifyMessageType.RollCall,
                                        ii.getId(),
                                        String.valueOf(ii.getRollCallId()),
                                        ii.getCalledEmployeeId(),
                                        RollCallMessageContentDocument.create(
                                                ii.getRollCallId(),
                                                ii.getActionOrganizationName(),
                                                ii.getActionEmployeeName()
                                        )
                                );
                            }).collect(Collectors.toList());
                    this.notifyMessageDocumentRepository.saveAll(messageDocuments);
                    Collection<RollCallEmployeeHistoryEntity> histories = new HashSet<>();
                    /**
                     * 获取CID信息
                     */
                    for (RollCallEmployeeEntity ee : callEmployees) {
                        try {
                            String selectedCid =
                                    selectedTenantUsers.stream()
                                            .filter(ii -> ii.getId().equalsIgnoreCase(ee.getCalledTenantUserId()) &&
                                                    StringUtils.hasLength(ii.getCid()) &&
                                                    !ii.getCid().contains("#")) /**CID不可能为#*/
                                            .map(ii -> ii.getCid())
                                            .findFirst().orElse(null);
                            ee.assignCalledTenantCid(selectedCid);
                            if (!StringUtils.hasLength(selectedCid)) {
                                RollCallEmployeeHistoryEntity history =
                                        ee.assignFailed2Call(
                                                RollCallType.RollCall,
                                                RollCallStatus.RollCallFailed, "点名失败-该用户未激活");

                                histories.add(history);
                            }
                        } catch (Exception ex) {
                            this.unitWork.rollback(tranId);
                            error.append(String.format("rollCallId=%s;Employee=%s;异常=%s;", ee.getRollCallId(), ee.getCalledEmployeeId(), ExceptionUtil.getStackMessage(ex)));
                            logger.error("Failed to roll call with BatchRollCallEmployeeCommand:rollCallId={};Employee={}", ee.getRollCallId(), ee.getCalledEmployeeId(), ex);
                        }
                    }

                    Collection<RollCallEmployeeEntity> validSelectedRollCallEmployees
                            = callEmployees.stream()
                            .filter(ii -> StringUtils.hasLength(ii.getCalledTenantUserCid()) && ii.getCalledTenantUserCid().length() > 3)
                            .collect(Collectors.toList());
                    /**
                     * 没有找到CID的时候自动进行失败
                     */
                    Collection<RollCallEmployeeEntity> selectedManuallyRollCallEmployees
                            = callEmployees.stream()
                            .filter(ii -> ii.getRollCallPlanId() == null && !StringUtils.hasLength(ii.getCalledTenantUserCid()))
                            .collect(Collectors.toList());

                    try {
                        this.eventDispatcher.dispatch(BatchDispatchRollCallEmployeeEvent.create(validSelectedRollCallEmployees));
                    }
                    catch (Exception ex) {
                        Collection<RollCallEmployeeEntity> badCallEmployees = validSelectedRollCallEmployees;
                        if (ex instanceof GeTuiBadTenantException gtException) {
                            badCallEmployees =
                                    validSelectedRollCallEmployees.stream().filter(ii ->
                                                    gtException.getDataItems().stream().anyMatch(ix -> ix.getCIds().contains(ii.getCalledTenantUserCid())))
                                            .collect(Collectors.toList());
                        }

                        for(RollCallEmployeeEntity ee: badCallEmployees) {
                            RollCallEmployeeHistoryEntity history =
                                    ee.assignFailed2Call(
                                            RollCallType.RollCall,
                                            RollCallStatus.RollCallFailed, "点名-个推执行异常");

                            histories.add(history);
                        }

                        selectedManuallyRollCallEmployees.addAll(badCallEmployees);
                    }

                    if (!CollectionUtils.isEmpty(selectedManuallyRollCallEmployees)) {
                        this.eventDispatcher.dispatch(ReplyRollCallEmployeeStatusEvent.create(
                                selectedManuallyRollCallEmployees
                        ));
                    }

                    this.rollCallEmployeeHistoryRepository.batchInsert(histories);
                    this.rollCallEmployeeRepository.batchUpdateStatusAndCId(callEmployees);
                    this.unitWork.commit(tranId);
                } catch (Exception ex) {
                    this.unitWork.rollback(tranId);
                    throw new BadTenantException(String.format("批量处理点名操作发生异常:%s", error), ex);
                }

                allRollCallEmployeeIds =
                        allRollCallEmployeeIds.stream()
                                .filter(ii -> !callEmployees.stream().anyMatch(ix -> ix.getId().equals(ii)))
                                .collect(Collectors.toList());

                logger.error("点名记录处理完成:{}",callEmployees.stream().map(ii->ii.getId()).collect(Collectors.joining(",")));
            }else {
                logger.error("等待业务({})提交到数据库....", allRollCallEmployeeIds.stream().collect(Collectors.joining(",")));
                try {
                    Thread.sleep(200);
                } catch (Exception ex) {
                }
            }

            retryIndex++;
            if (retryIndex > 30) {
                logger.error("RollCall Employee数据处理异常, 异常数据为:{}",
                        allRollCallEmployeeIds.stream().map(ii -> String.format("'%s'", ii)).collect(Collectors.joining(","))
                );
            }
        }
    }

    @Override
    public void dispatch(ReplyRollCallEmployeeStatusCommand command) {
        Collection<RollCallEmployeeEntity> rollCallEmployees =
                this.rollCallEmployeeRepository.findByRollCallIdAndEmployeeIds(command.getId(), command.getEmployeeIds());
        if (CollectionUtils.isEmpty(rollCallEmployees)) {
            /**
             * 主要避免两个人共用一个手机切换登入的时候, 由于Cid是一样的, 导致另外一个人收到个推的消息之后点击确认保这个错误
             */
            logger.error("ReplyRollCallEmployeeStatusCommand: Failed to find the roll call employees with callId={} and empIds={}", command.getId(),
                    command.getEmployeeIds().stream().collect(Collectors.joining(","))
            );
            return;
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            Collection<RollCallEmployeeHistoryEntity> employeeHistories = new ArrayList<>();
            for (RollCallEmployeeEntity ee : rollCallEmployees) {
                Collection<RollCallEmployeeHistoryEntity> histories
                        = ee.reply(
                        command.getCallType(),
                        ee.getLastRoomId(),
                        command.getCallStatus());

                employeeHistories.addAll(histories);
            }

            /**
             * 仅针对手动点名轮换的服务
             */
            Collection<RollCallEmployeeEntity> selectedManuallyRollCallEmployees =
                    rollCallEmployees.stream().filter(ii -> ii.getRollCallPlanId() == null)
                            .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(selectedManuallyRollCallEmployees)) {
                this.eventDispatcher.dispatch(ReplyRollCallEmployeeStatusEvent.create(
                        selectedManuallyRollCallEmployees
                ));
            }

            this.rollCallEmployeeHistoryRepository.batchInsert(employeeHistories);
            this.rollCallEmployeeRepository.batchUpdateStatusAndCId(rollCallEmployees);

            this.unitWork.commit(tranId);
        } catch (IgnoreTenantException ix) {
            this.unitWork.rollback(tranId);
            logger.error("Ignore exception: 该请求(RollCallId={},employeeIds={})已经被确认过; 无需重复确认",
                    command.getId(), command.getEmployeeIds().stream().collect(Collectors.joining(",")));
        } catch (TenantExceptionAbstract ex) {
            this.unitWork.rollback(tranId);
            throw ex;
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);
            logger.error("Failed to roll call with-ReplyRollCallEmployeeStatusCommand:{}-{}", command.getCallType(), command.getCallStatus(), ex);
            throw new BadTenantException("确认点名信息-发生异常", ex);
        }
    }

    @Override
    public void dispatch(RollCallBatchDispatchCommand command) {
        Collection<Long> rollCallIds = command.getItems().stream()
                .map(ii -> ii.getRollCallId()).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(rollCallIds)) {
            return;
        }

        Collection<String> employeeIds = command.getItems().stream()
                .flatMap(ii -> ii.getEmployeeIds().stream())
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(employeeIds)) {
            return;
        }

        Collection<RollCallEmployeeEntity> rollCallEmployees =
                this.rollCallEmployeeRepository.findByRollCallIdsAndEmployeeIds(rollCallIds, employeeIds);
        if (CollectionUtils.isEmpty(rollCallEmployees)) {
            return;
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            Collection<RollCallEmployeeHistoryEntity> employeeHistories = new HashSet<>();
            for (RollCallEmployeeEntity ree : rollCallEmployees) {
                String selectedRoomId = command.getItems().stream()
                        .filter(ii -> ii.getRollCallId().equals(ree.getRollCallId()) && ii.getEmployeeIds().contains(ree.getCalledEmployeeId()))
                        .map(ii -> ii.getRoomId())
                        .findFirst().orElse(null);
                RollCallEmployeeHistoryEntity history = ree.doDispatch(selectedRoomId);

                if (history != null) {
                    employeeHistories.add(history);
                }
            }


            if (!CollectionUtils.isEmpty(employeeHistories)) {
                this.rollCallEmployeeHistoryRepository.batchInsert(employeeHistories);
                this.rollCallEmployeeRepository.batchUpdateStatusAndCId(rollCallEmployees);
            }

            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);
            logger.error("Failed to roll call with-RollCallBatchDispatchCommand:{}",
                    rollCallIds.stream().map(ii -> String.valueOf(ii)).collect(Collectors.joining(",")), ex);

            throw new BadTenantException("督导点名发生异常", ex);
        }
    }

    @Override
    public CancelRollCallEmployeeCommandResult dispatch(CancelRollCallEmployeeCommand command) {
        Collection<RollCallEmployeeEntity> selectedRollCallEmployees =
                this.rollCallEmployeeRepository.getTopNCanBeExpiredRecords(200, command.getCallStatus());
        if (!CollectionUtils.isEmpty(selectedRollCallEmployees)) {
            Collection<RollCallEmployeeHistoryEntity> employeeHistories = new ArrayList<>();
            for (RollCallEmployeeEntity ee : selectedRollCallEmployees) {
                Collection<RollCallEmployeeHistoryEntity> histories =
                        ee.autoFailed(command.getCallStatus() == RollCallStatus.RollCallInit ? "自动点名失败" : "督导点名失败");

                if (histories != null) {
                    employeeHistories.addAll(histories);
                }
            }

            if (!CollectionUtils.isEmpty(employeeHistories)) {
                String tranId = this.unitWork.beginTransaction();
                try {
                    this.rollCallEmployeeRepository.batchUpdateStatusAndCId(selectedRollCallEmployees);
                    this.rollCallEmployeeHistoryRepository.batchInsert(employeeHistories);
                    /**
                     * 收到和确认之后;自动触发这个
                     */
                    Collection<RollCallEmployeeEntity> selectedManuallyRollCallEmployees
                            = selectedRollCallEmployees.stream().filter(ii -> ii.getRollCallPlanId() == null)
                            .collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(selectedManuallyRollCallEmployees)) {
                        this.eventDispatcher.dispatch(ReplyRollCallEmployeeStatusEvent.create(
                                selectedManuallyRollCallEmployees
                        ));
                    }

                    this.unitWork.commit(tranId);
                } catch (Exception ex) {
                    this.unitWork.rollback(tranId);
                    logger.error("Failed to roll call with-CancelRollCallEmployeeCommand:{}", command.getCallStatus(), ex);
                    throw new BadTenantException("自动点名失败发生异常", ex);
                }
            }
        }

        return CancelRollCallEmployeeCommandResult.create(
                selectedRollCallEmployees.stream()
                        .map(ii -> CancelRollCallEmployeeCommandResult.CancelRollCallEmployeeCommandItem.create(ii.getId(), ii.getStatus())
                        ).collect(Collectors.toList())
        );
    }

    @Override
    public void dispatch(RollCallBatchEmployeeRoomActionCommand command) {
        if (CollectionUtils.isEmpty(command.getItems())) {
            return;
        }

        Collection<String> employeeIds =
                command.getItems().stream().map(ii -> ii.getEmployeeId()).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(employeeIds)) {
            return;
        }

        Collection<Long> rollCallIds =
                command.getItems().stream().map(ii -> ii.getRollCallId()).collect(Collectors.toList());

        Collection<RollCallEmployeeEntity> rollCallEmployees =
                this.rollCallEmployeeRepository.findByRollCallIdsAndEmployeeIds(rollCallIds, employeeIds);
        if (CollectionUtils.isEmpty(rollCallEmployees)) {
            return;
        }

        String tranId = this.unitWork.beginTransaction();
        try {
            Collection<RollCallEmployeeHistoryEntity> employeeHistories = new ArrayList<>();
            for (RollCallEmployeeEntity ee : rollCallEmployees) {
                RollCallBatchEmployeeRoomActionCommand.RollCallReplyEmployeeCommandItem rollCallReplyEmployeeCommandItem =
                        command.getItems().stream().filter(ii -> ii.getRollCallId().equals(ee.getRollCallId()) && ii.getEmployeeId().equals(ee.getCalledEmployeeId()))
                                .findFirst().orElse(null);
                if (rollCallReplyEmployeeCommandItem == null) {
                    throw new BadTenantException("异常参数信息: rollCallStatus=null");
                }

                Collection<RollCallEmployeeHistoryEntity> histories
                        = ee.reply(
                        RollCallType.Dispatch,
                        StringUtils.hasLength(ee.getLastRoomId()) ? ee.getLastRoomId() : rollCallReplyEmployeeCommandItem.getRoomId(),
                        rollCallReplyEmployeeCommandItem.getCallStatus()
                );

                employeeHistories.addAll(histories);
            }

            this.rollCallEmployeeHistoryRepository.batchInsert(employeeHistories);
            this.rollCallEmployeeRepository.batchUpdateStatusAndCId(rollCallEmployees);

            Collection<String> instantActiveDocIds =
                    command.getItems().stream().map(ii -> ii.getInstantActiveDocId()).distinct()
                            .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(instantActiveDocIds)) {
                this.instantActivityDataDocumentRepository.deleteAllById(instantActiveDocIds);
            }

            this.unitWork.commit(tranId);
        } catch (TenantExceptionAbstract ex) {
            this.unitWork.rollback(tranId);
            throw ex;
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);
            throw new BadTenantException("确认点名信息-发生异常", ex);
        }
    }

    private List<RdEmployeeDocument> getMatchEmployees(CreateRollCallCommand command) {
        SearchStream<RdEmployeeDocument> searchStream =
                this.entityStream.of(RdEmployeeDocument.class);

        if (command.getOperator() == null) {
            throw new BadTenantException(String.format("系统参数异常(planId=%s), 操作人员不能为空", command.getRollCallPlanId()));
        }

        command.getOperator().validate();

        searchStream =
                DocumentScopeFilterUtils.basicFilterEmployee(searchStream,
                        command.getOperator().getActionOrgInstitutional(),
                        command.getOperator().getActionOrganizationId(),
                        command.getOperator().getActionOrganizationName(),
                        command.getSuperviseDepartIds()
                );

        if (!CollectionUtils.isEmpty(command.getStationTypes())) {
            SearchStream<RdSecurityStationDocument> securityStationDocumentSearchStream
                    = this.entityStream.of(RdSecurityStationDocument.class);

            securityStationDocumentSearchStream =
                    DocumentScopeFilterUtils.basicFilterStation(
                            securityStationDocumentSearchStream,
                            command.getOperator().getActionOrgInstitutional(),
                            command.getOperator().getActionOrganizationId(),
                            command.getSuperviseDepartIds()
                    );

            String[] stationTypes = command.getStationTypes().toArray(ix -> new String[ix]);
            securityStationDocumentSearchStream = securityStationDocumentSearchStream.filter(
                    RdEmployeeDocument$.STATION_TYPES.in(stationTypes)
            );

            if (!CollectionUtils.isEmpty(command.getSecurityStationIds())) {
                String[] stationIds = command.getSecurityStationIds().toArray(ix -> new String[ix]);
                securityStationDocumentSearchStream = securityStationDocumentSearchStream.filter(
                        RdSecurityStationDocument$.ID.in(stationIds)
                );
            }

            Collection<String> selectedStationIds = new HashSet<>();
            selectedStationIds =
                    securityStationDocumentSearchStream
                            .collect(Collectors.toList())
                            .stream().map(ii -> ii.getId())
                            .collect(Collectors.toList());

            /**
             * 搜索不到驻勤点
             */
            if (CollectionUtils.isEmpty(selectedStationIds)) {
                return Collections.EMPTY_LIST;
            }

            String[] stationIds = selectedStationIds.toArray(ix -> new String[ix]);
            searchStream = searchStream.filter(RdEmployeeDocument$.SECURITY_STATION_ID.in(stationIds));
        } else if (!CollectionUtils.isEmpty(command.getSecurityStationIds())) {
            Collection<String> selectedStationIds = command.getSecurityStationIds();
            if (!CollectionUtils.isEmpty(selectedStationIds)) {
                String[] stationIds = selectedStationIds.toArray(ix -> new String[ix]);
                searchStream = searchStream.filter(RdEmployeeDocument$.SECURITY_STATION_ID.in(stationIds));
            }
        }

        return searchStream
                /**
                 * 这个一定要再map之前, 否则无法执行
                 */
                .limit(RedisConfig.MAX_LIMIT)
                .collect(Collectors.toList());
    }

    private List<RollCallEmployeeEntity> getMatchValidBatchEmployees(Collection<String> data) {
        if (CollectionUtils.isEmpty(data)) {
            return Collections.EMPTY_LIST;
        }

        Collection<RollCallEmployeeEntity> callEmployees =
                data.stream()
                        .map(ii -> this.jsonProvider.toObject(RollCallEmployeeEntity.class, ii))
                        .collect(Collectors.toList());
        Collection<Long> rollCallIds =
                callEmployees.stream()
                        .map(ii -> ii.getRollCallId()).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(rollCallIds)) {
            return Collections.EMPTY_LIST;
        }

        Collection<RollCallEntity> existRollCalls =
                this.rollCallRepository.getByIds(rollCallIds);

        int retryIndex = 0;
        Collection<Long> existsRollCallIds = existRollCalls.stream()
                .map(ii -> ii.getId()).collect(Collectors.toList());
        Collection<Long> notExistRollCallIds =
                rollCallIds.stream().filter(ii -> !existsRollCallIds.stream().anyMatch(ix -> ix.equals(ii)))
                        .collect(Collectors.toList());

        while (notExistRollCallIds.size() > 0 && retryIndex < 10) {

            /**
             * 找不到RollCall的情况; 标识数据库还没提交; 因此, 进行等待重试
             */
            try {
                Thread.sleep(100);
                logger.error("正在处理RollCall数据({}); 等待重试中...", notExistRollCallIds.stream().map(ii->String.valueOf(ii)).collect(Collectors.joining(",")));
            } catch (Exception ex) {
            }

            existRollCalls =
                    this.rollCallRepository.getByIds(notExistRollCallIds);
            existsRollCallIds.addAll(existRollCalls.stream().map(ii -> ii.getId()).collect(Collectors.toList()));

            rollCallIds.stream().filter(ii -> !existsRollCallIds.stream().anyMatch(ix -> ix.equals(ii)))
                    .collect(Collectors.toList());
            retryIndex++;
        }

        Collection<Long> finalExistRollCalls = existsRollCallIds;
        return callEmployees.stream()
                .filter(ii -> finalExistRollCalls.stream().anyMatch(ix -> ix.equals(ii.getRollCallId())))
                .collect(Collectors.toList());
    }
}
