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

import com.bcxin.tenant.open.domains.dtos.CalculateCommunicatedDTO;
import com.bcxin.tenant.open.domains.entities.ConfigOfBillEntity;
import com.bcxin.tenant.open.domains.entities.DeviceCommunicatedLogDetailEntity;
import com.bcxin.tenant.open.domains.entities.DeviceCommunicatedLogEntity;
import com.bcxin.tenant.open.domains.entities.RoomUserEntity;
import com.bcxin.tenant.open.domains.repositories.*;
import com.bcxin.tenant.open.domains.services.DeviceCommunicatedLogService;
import com.bcxin.tenant.open.domains.services.commands.CreateDeviceCommunicatedLogCommand;
import com.bcxin.tenant.open.domains.services.commands.CreateSyncDeviceCommunicatedLogCommand;
import com.bcxin.tenant.open.domains.utils.PointUtils;
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.exceptions.BadTenantException;
import com.bcxin.tenant.open.infrastructures.exceptions.NoFoundTenantException;
import com.bcxin.tenant.open.infrastructures.utils.StringUtil;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.log4j2.Log4j2LoggerImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

@Service
public class DeviceCommunicatedLogServiceImpl implements DeviceCommunicatedLogService {
    private static Logger logger = LoggerFactory.getLogger(DeviceCommunicatedLogServiceImpl.class);
    private final DeviceCommunicatedLogRepository deviceCommunicatedLogRepository;
    private final DeviceCommunicatedLogDetailRepository deviceCommunicatedLogDetailRepository;

    private final RoomRepository roomRepository;
    private final UnitWork unitWork;
    private final JsonProvider jsonProvider;
    private final IdWorker idWorker;
    private final ConfigOfBillRepository configOfBillRepository;

    public DeviceCommunicatedLogServiceImpl(DeviceCommunicatedLogRepository deviceCommunicatedLogRepository,
                                            DeviceCommunicatedLogDetailRepository deviceCommunicatedLogDetailRepository,
                                            RoomRepository roomRepository, UnitWork unitWork, JsonProvider jsonProvider,
                                            IdWorker idWorker, ConfigOfBillRepository configOfBillRepository) {
        this.deviceCommunicatedLogRepository = deviceCommunicatedLogRepository;
        this.deviceCommunicatedLogDetailRepository = deviceCommunicatedLogDetailRepository;
        this.roomRepository = roomRepository;
        this.unitWork = unitWork;
        this.jsonProvider = jsonProvider;
        this.idWorker = idWorker;
        this.configOfBillRepository = configOfBillRepository;
    }

    @Override
    public void dispatch(CreateDeviceCommunicatedLogCommand command) {
        Collection<DeviceCommunicatedLogDetailEntity> pendingLogDetails =
                command.getDeviceCommunicatedLogDetails();
        if (CollectionUtils.isEmpty(pendingLogDetails)) {
            return;
        }

        /**
         * 有效的被调度人才会生成调度记录
         * 调度人的CalledDomainId是空的
         */
        Collection<DeviceCommunicatedLogDetailEntity> toBeSavedLogDetails
                = pendingLogDetails.stream().filter(ii -> ii.getCalledDomainId() != null)
                .collect(Collectors.toList());

        Collection<String> calledDomainIds = toBeSavedLogDetails.stream()
                .filter(ix -> StringUtils.hasLength(ix.getCalledDomainId()))
                .map(ii -> ii.getCalledDomainId())
                .distinct()
                .collect(Collectors.toList());

        Collection<DeviceCommunicatedLogEntity> logs =
                calledDomainIds.stream().map(calledDomainId -> {
                    Collection<DeviceCommunicatedLogDetailEntity> selectedLogDetails
                            = toBeSavedLogDetails.stream().filter(ii -> !StringUtil.isEmpty(ii.getCalledDomainId()) &&
                                    ii.getCalledDomainId().equalsIgnoreCase(calledDomainId))
                            .collect(Collectors.toList());

                    /**
                     * 最小值
                     */
                    Optional<DeviceCommunicatedLogDetailEntity> firstSelectedLogOptional =
                            selectedLogDetails.stream()
                                    .sorted((i1, i2) -> {
                                        if (i1.getBeginTime() == null) {
                                            return 0;
                                        }

                                        if (i2.getBeginTime() == null) {
                                            return 0;
                                        }

                                        int flag = i1.getBeginTime().compareTo(i2.getBeginTime());

                                        return flag;
                                    }).findFirst();

                    if (!firstSelectedLogOptional.isPresent()) {
                        throw new NoFoundTenantException(String.format("找不到该企业log日志:%s", calledDomainId));
                    }

                    DeviceCommunicatedLogDetailEntity selectedLog = firstSelectedLogOptional.get();

                    /**
                     * 最大值
                     */
                    Optional<DeviceCommunicatedLogDetailEntity> lastSelectedLogOptional =
                            toBeSavedLogDetails.stream().filter(ix -> StringUtils.hasLength(ix.getCalledDomainId()))
                                    .filter(ii -> ii.getCalledDomainId().equalsIgnoreCase(calledDomainId))
                                    .sorted((i1, i2) -> {
                                        if (i1.getBeginTime() == null) {
                                            return 0;
                                        }

                                        if (i2.getBeginTime() == null) {
                                            return 0;
                                        }

                                        return i2.getBeginTime().compareTo(i1.getBeginTime());
                                    })
                                    .findFirst();
                    if (!lastSelectedLogOptional.isPresent()) {
                        throw new BadTenantException("无效的通讯日志信息");
                    }

                    /**
                     * 取出本企业的最大调度时间-取出同一个总共接听的最多时间值
                     */
                    Collection<DeviceCommunicatedLogDetailEntity> calculatedLogs =
                            toBeSavedLogDetails.stream().filter(ix -> ix.getCalledDomainId().equalsIgnoreCase(calledDomainId)
                                    && ix.getBeginTime() != null).collect(Collectors.toList());

                    int dispatchTimeInMinuteInTotal = calculateTotalMinutesWithCommunicatedLogs(calculatedLogs);

                    int pointsInTotal
                            = toBeSavedLogDetails.stream().filter(ix -> ix.getCalledDomainId().equalsIgnoreCase(calledDomainId))
                            .collect(Collectors.summingInt(ix -> ix.getPoints()));

                    String domainHash = selectedLog.getCalledDomainId();
                    DeviceCommunicatedLogEntity communicatedLog =
                            DeviceCommunicatedLogEntity.create(
                                    selectedLog.getDeskType(),
                                    selectedLog.getPkId(),
                                    String.format("%s_%s",
                                            selectedLog.getRoomId(),
                                            domainHash),
                                    selectedLog.getBeginTime(),
                                    lastSelectedLogOptional.get().getEndTime(),
                                    selectedLog.getCommunicatedType(),
                                    selectedLog.getCallerUserId(),
                                    selectedLog.getCallerUserName(),
                                    selectedLog.getCallerCompanyName(),
                                    selectedLog.getCallerDevicePkId(),
                                    selectedLog.getCallerDeviceNo(),
                                    selectedLog.getCalledDomainId(),
                                    selectedLog.getDomainId(),
                                    dispatchTimeInMinuteInTotal,
                                    pointsInTotal,
                                    selectedLog.getCalledCompanyName(),
                                    selectedLog.getRoomId(),
                                    selectedLog.getReferenceType(),
                                    selectedLog.getReferenceNumber(),
                                    selectedLog.isPaidByCaller(),
                                    selectedLog.getCallerOrgInstitutional()
                            );

                    logger.error("当前企业的调度时间为:{} id={};roomId={};时间段={};", dispatchTimeInMinuteInTotal, communicatedLog.getId(),
                            selectedLog.getRoomId(),
                            calculatedLogs.stream().map(ii ->
                                    String.format("beginTime=%s;endTime=%s;", ii.getBeginTime(), ii.getEndTime())
                            ).collect(Collectors.joining(";"))
                    );

                    selectedLogDetails.forEach(ix -> {
                        ix.assignLogId(communicatedLog.getId());
                    });

                    return communicatedLog;
                }).collect(Collectors.toList());

        String tranId = this.unitWork.beginTransaction();
        try {
            this.deviceCommunicatedLogRepository.batchInsert(logs);
            this.deviceCommunicatedLogDetailRepository.batchInsert(toBeSavedLogDetails);
            this.unitWork.commit(tranId);
        } catch (Exception ex) {
            this.unitWork.rollback(tranId);
            throw ex;
        }
    }

    @Override
    public void dispatch(CreateSyncDeviceCommunicatedLogCommand command) {
        String tranId = this.unitWork.beginTransaction();
        try {
            DeviceCommunicatedLogEntity deviceCommunicatedLog = this.jsonProvider.toObject(DeviceCommunicatedLogEntity.class, command.getContent());
            if (deviceCommunicatedLog == null) {
                throw new BadTenantException(String.format("无效数据格式:%s", command.getContent()));
            }

            if (this.deviceCommunicatedLogRepository.getByNoPkId(deviceCommunicatedLog.getId()) != null) {
                this.deviceCommunicatedLogRepository.update(deviceCommunicatedLog);
            } else {
                this.deviceCommunicatedLogRepository.insert(deviceCommunicatedLog);
            }

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

    private int calculateTotalMinutesWithCommunicatedLogs(Collection<DeviceCommunicatedLogDetailEntity> logs) {
        if (CollectionUtils.isEmpty(logs)) {
            return 0;
        }

        logs = logs.stream().sorted((i1, i2) -> {
            if (i1.getBeginTime() == null) {
                return 0;
            }

            if (i2.getBeginTime() == null) {
                return 0;
            }

            return i1.getBeginTime().compareTo(i2.getBeginTime());
        }).collect(Collectors.toList());
        Collection<CalculateCommunicatedDTO> calculateCommunicatedDTOS = new ArrayList<>();

        logs.forEach(ic -> {
            /**
             * 最早的有包含其他的时间段的时候; 计算交集的时间段
             * ii.getBeginTime().before(ic.getBeginTime()) &&
             * ic.getBeginTime().before(ii.getEndTime())
             * 则说明时间段是有重叠的
             * 12:00~12:30
             * 12:20~12:50
             * 那么取出的结果为: 12:00~12:50
             *
             * ic.getEndTime为空; 则说明该记录为未接听的记录
             */
            Optional<CalculateCommunicatedDTO> ccDoptional =
                    calculateCommunicatedDTOS.stream().filter(ii ->
                            ii.getBeginTime().before(ic.getBeginTime()) &&
                                    (ii.getEndTime() != null && ic.getBeginTime().before(ii.getEndTime()))
                    ).findFirst();

            if (!ccDoptional.isPresent()) {
                /**
                 * changed by lhc: 2023-10-11; 都不相同的时候才加入列表
                 */
                if (!calculateCommunicatedDTOS.stream().anyMatch(ii -> ii.getBeginTime().equals(ic.getBeginTime()) &&
                        (ii.getEndTime() == null || ic.getEndTime() == null || ii.getEndTime().equals(ic.getEndTime()))
                )
                ) {
                    calculateCommunicatedDTOS.add(CalculateCommunicatedDTO.create(ic.getBeginTime(), ic.getEndTime(), ic.getCalledUserId()));
                }
            } else if (ic.getEndTime() != null && ccDoptional.get().getEndTime().before(ic.getEndTime())) {
                ccDoptional.get().updateSwitchMaxEndTime(ic.getEndTime());
            }
        });


        /**
         * 分组之后; 针对有接听和未接听再次进行核对; 如果已接听的分钟已经涵盖未接听的情况; 那么可以忽略; 否则
         */
        Collection<CalculateCommunicatedDTO> answeredCommunicatedDTOS
                = calculateCommunicatedDTOS.stream().filter(ii -> ii.getBeginTime() != null && ii.getEndTime() != null)
                .collect(Collectors.toList());
        int sumInAnswer = answeredCommunicatedDTOS.stream()
                .map(ii -> PointUtils.translate2DispatchTimeInMinutes(ii.getBeginTime(), ii.getEndTime()))
                .collect(Collectors.summingInt(ii -> ii));

        int sumWithoutAnswer = 0;
        Collection<CalculateCommunicatedDTO> noAnsweredCommunicatedDTOS
                = calculateCommunicatedDTOS.stream().filter(ii -> ii.getEndTime() == null)
                .collect(Collectors.toList());
        for (CalculateCommunicatedDTO noAnswer : noAnsweredCommunicatedDTOS) {
            if (answeredCommunicatedDTOS.stream().anyMatch(ix -> ix.getBeginTime().before(noAnswer.getBeginTime()))) {
                //todo 有接听的时候; 如果有涵盖该范围; 则不用计入企业的总分钟数量
            } else {
                CalculateCommunicatedDTO firstSelectedCalculateCommunicated =
                        answeredCommunicatedDTOS.stream().filter(ii -> ii.getBeginTime().after(noAnswer.getBeginTime()))
                                .sorted((i1, i2) -> {
                                    if (i1.getBeginTime() == null) {
                                        return 0;
                                    }

                                    if (i2.getBeginTime() == null) {
                                        return 0;
                                    }

                                    int flag = i1.getBeginTime().compareTo(i2.getBeginTime());

                                    return flag;
                                }).findFirst().orElse(null);
                if (firstSelectedCalculateCommunicated != null) {
                    int selectedInMinutes =
                            PointUtils.translate2DispatchTimeInMinutes(firstSelectedCalculateCommunicated.getBeginTime(), firstSelectedCalculateCommunicated.getEndTime());
                    /**
                     * 距离未接听的分钟数计算的结果如果比接近的接听的分钟数大于1； 那么按照一分钟来算
                     */
                    int selectedNoAnswerInMinutes =
                            PointUtils.translate2DispatchTimeInMinutes(noAnswer.getBeginTime(), firstSelectedCalculateCommunicated.getEndTime());
                    if (selectedNoAnswerInMinutes - selectedInMinutes > 0) {
                        sumWithoutAnswer += 1;
                    }
                }
            }
        }

        int sum = sumInAnswer + sumWithoutAnswer;

        return sum;
    }
}