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

import com.bcxin.tenant.open.document.domains.repositories.RoomDocumentRepository;
import com.bcxin.tenant.open.domains.BillPaymentRuleConfig;
import com.bcxin.tenant.open.domains.dtos.ProcessTencentCallbackLogDTO;
import com.bcxin.tenant.open.domains.dtos.RoomCommunicatedGroupDTO;
import com.bcxin.tenant.open.domains.entities.RoomUserEntity;
import com.bcxin.tenant.open.domains.entities.TencentCallbackLogEntity;
import com.bcxin.tenant.open.domains.events.PreparedRoomCommunicatedGroupEvent;
import com.bcxin.tenant.open.domains.repositories.RoomUserRepository;
import com.bcxin.tenant.open.domains.repositories.TencentCallbackLogRepository;
import com.bcxin.tenant.open.domains.services.TencentCallbackLogService;
import com.bcxin.tenant.open.domains.services.commands.CreateTencentCallbackLogCommand;
import com.bcxin.tenant.open.domains.services.commands.ProcessTencentCallbackLogCommand;
import com.bcxin.tenant.open.domains.utils.RoomCommunicatedGroupUtils;
import com.bcxin.tenant.open.domains.views.TencentCallbackLogView;
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.enums.ProcessedStatus;
import com.bcxin.tenant.open.infrastructures.events.EventDispatcher;
import com.bcxin.tenant.open.infrastructures.exceptions.BadTenantException;
import com.bcxin.tenant.open.infrastructures.utils.ExceptionUtil;
import com.bcxin.tenant.open.infrastructures.utils.NumberUtil;
import com.redis.om.spring.search.stream.EntityStream;
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.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

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

    private final TencentCallbackLogRepository tencentCallbackLogRepository;
    private final EventDispatcher eventDispatcher;
    private final RoomDocumentRepository roomDocumentRepository;
    private final RoomUserRepository roomUserRepository;
    private final JsonProvider jsonProvider;
    private final IdWorker idWorker;
    private final UnitWork unitWork;
    private final EntityStream entityStream;
    private final BillPaymentRuleConfig billPaymentRuleConfig;

    public TencentCallbackLogServiceImpl(TencentCallbackLogRepository tencentCallbackLogRepository,
                                         EventDispatcher eventDispatcher,
                                         RoomDocumentRepository roomDocumentRepository,
                                         RoomUserRepository roomUserRepository,
                                         JsonProvider jsonProvider,
                                         IdWorker idWorker,
                                         UnitWork unitWork, EntityStream entityStream,
                                         BillPaymentRuleConfig billPaymentRuleConfig) {
        this.tencentCallbackLogRepository = tencentCallbackLogRepository;
        this.eventDispatcher = eventDispatcher;
        this.roomUserRepository = roomUserRepository;
        this.jsonProvider = jsonProvider;
        this.unitWork = unitWork;
        this.idWorker = idWorker;
        this.roomDocumentRepository = roomDocumentRepository;
        this.entityStream = entityStream;
        this.billPaymentRuleConfig = billPaymentRuleConfig;
    }

    @Override
    public void dispatch(CreateTencentCallbackLogCommand command) {
        Long eventGroupId = NumberUtil.toLong(getByKey(command.getData(), "EventGroupId"));
        Integer eventType = NumberUtil.toInteger(getByKey(command.getData(), "EventType"));
        Long callbackTs = NumberUtil.toLong(getByKey(command.getData(), "CallbackTs"));
        Map<String, Object> eventInfo = (Map<String, Object>) getByKey(command.getData(), "EventInfo");
        String userId = null;
        String roomId = null;
        if (eventInfo != null) {
            userId = String.valueOf(getByKey(eventInfo, "UserId"));
            roomId = String.valueOf(getByKey(eventInfo, "RoomId"));
        }

        String sdkAppId = (String) getByKey(command.getData(), "SdkAppId");

        TencentCallbackLogEntity entity =
                TencentCallbackLogEntity.create(
                        eventGroupId,
                        eventType,
                        callbackTs,
                        this.jsonProvider.getJson(eventInfo),
                        userId,
                        roomId,
                        sdkAppId
                );

        this.tencentCallbackLogRepository.insert(entity);
    }

    /**
     * 根据腾讯音视频的回调信息来产生联动值的消费记录及更新企业的联动值信息
     * -交易记录
     * -通讯日志
     * @param command
     */
    @Override
    public void dispatch(ProcessTencentCallbackLogCommand command) {
        if (CollectionUtils.isEmpty(command.getRoomIds())) {
            return;
        }

        Collection<Long> roomIds = command.getRoomIds().stream().distinct().collect(Collectors.toList());
        logger.info("已经推出并满足符合条件的房间Ids={}", roomIds);

        Collection<TencentCallbackLogView> roomRelatedData =
                tencentCallbackLogRepository.getAllPendingLogsByRoomIds(roomIds);
        Collection<RoomUserEntity> roomUsers =
                this.roomUserRepository.getRoomUsersByRoomIds(roomIds);

        Collection<RoomCommunicatedGroupDTO> communicatedGroups =
                RoomCommunicatedGroupUtils.build(
                        roomDocumentRepository,
                        jsonProvider, roomIds,
                        roomRelatedData,roomUsers,
                        entityStream,
                        billPaymentRuleConfig
                );
        RoomCommunicatedGroupUtils.appendNoApplyRoomUsers(
                idWorker,
                communicatedGroups, roomUsers
        );

        if (CollectionUtils.isEmpty(communicatedGroups)) {
            String error = RoomCommunicatedGroupUtils.getCurrentProcessError();
            processErrorRoomIds(roomIds, StringUtils.hasLength(error) ? error : "1.无效数据或者无效房间数据, 无法找到开启房间的发起人信息[RoomCommunicatedGroupUtils.build]");
            return;
        }

        Collection<Long> invalidRoomIds =
                roomIds.stream()
                        .filter(ix -> !communicatedGroups.stream().anyMatch(iz -> iz.getRoomId().equalsIgnoreCase(String.valueOf(ix))))
                        .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(invalidRoomIds)) {
            String error = RoomCommunicatedGroupUtils.getCurrentProcessError();
            processErrorRoomIds(invalidRoomIds, StringUtils.hasLength(error) ? error : "2.无效数据或者无效房间数据, 无法找到开启房间的发起人信息[RoomCommunicatedGroupUtils.build]");
        }

        /**
         * 根据各个房间进行联动值的计算
         */
        for (Long selectedRoomId : roomIds) {
            Collection<RoomCommunicatedGroupDTO> selectedCommunicatedGroups =
                    communicatedGroups.stream().filter(ix -> ix.getRoomId().equalsIgnoreCase(String.valueOf(selectedRoomId)))
                            .collect(Collectors.toList());

            if(selectedCommunicatedGroups.size()>1) {
                throw new BadTenantException("一个房间不应该存在多个RoomCommunicatedGroup对象");
            }

            try {
                processDispatch(selectedCommunicatedGroups.stream().findFirst().orElse(null),
                        selectedRoomId);
            } catch (Exception ex) {
                processErrorRoomIds(Collections.singleton(selectedRoomId), ExceptionUtil.getStackMessage(ex));
                logger.error("房间信息计算错误:{};异常:{}", selectedRoomId, ExceptionUtil.getStackMessage(ex));
            }
        }
    }

    public void processDispatch(RoomCommunicatedGroupDTO cg,Long selectedRoomId) {
        try {
            StringBuilder error = new StringBuilder();
            String tranId = this.unitWork.beginTransaction();
            Collection<Long> roomIds = Collections.singleton(selectedRoomId);
            try {
                Collection<RoomCommunicatedGroupDTO> communicatedGroups = Collections.singleton(cg);
                logger.info("构建的交流房间数量={}", communicatedGroups.size());
                ProcessTencentCallbackLogDTO processTencentCallbackLogDTO = null;
                if (CollectionUtils.isEmpty(cg.getAudioVideoLogs())) {
                    processTencentCallbackLogDTO =
                            ProcessTencentCallbackLogDTO.create(roomIds, ProcessedStatus.Done, "该房间未开始音视频的调度");
                } else if (CollectionUtils.isEmpty(cg.getJoinRoomUsers())) {
                    processTencentCallbackLogDTO =
                            ProcessTencentCallbackLogDTO.create(roomIds, ProcessedStatus.Done, "该房间未有人员参与进来");
                } else {
                    this.eventDispatcher.dispatch(PreparedRoomCommunicatedGroupEvent.create(communicatedGroups));
                    processTencentCallbackLogDTO =
                            ProcessTencentCallbackLogDTO.create(roomIds, ProcessedStatus.Done, "完成");
                }

                this.tencentCallbackLogRepository.process(processTencentCallbackLogDTO);
                this.unitWork.commit(tranId);
            } catch (Exception ex) {
                ex.printStackTrace();

                this.unitWork.rollback(tranId);

                tranId = this.unitWork.beginTransaction();
                try {
                    ProcessTencentCallbackLogDTO processTencentCallbackLogDTO =
                            ProcessTencentCallbackLogDTO.create(roomIds, ProcessedStatus.Error, ExceptionUtil.getStackMessage(ex));

                    this.tencentCallbackLogRepository.process(processTencentCallbackLogDTO);
                    this.unitWork.commit(tranId);
                } catch (Exception e) {
                    this.unitWork.rollback(tranId);
                    e.printStackTrace();
                }

                error.append(String.format("执行房间(%s) 发生异常:%s;", cg.getRoomId(), ExceptionUtil.getStackMessage(ex)));
            }

            if (error.length() > 1) {
                throw new BadTenantException(error.toString());
            }
        } catch (Exception ex) {
            throw ex;
        }
    }

    public void processErrorRoomIds(Collection<Long> roomIds,String ex)
    {
        String tranId =
                this.unitWork.beginTransaction();
        try {
            ProcessTencentCallbackLogDTO processTencentCallbackLogDTO =
                    ProcessTencentCallbackLogDTO.create(roomIds, ProcessedStatus.Error, ex);

            this.tencentCallbackLogRepository.process(processTencentCallbackLogDTO);
            this.unitWork.commit(tranId);
        } catch (Exception e) {
            this.unitWork.rollback(tranId);
            e.printStackTrace();
        }
    }

    private Object getByKey(Map<String,Object> data,String key) {
        if (key == null || data == null) {
            return null;
        }

        Optional<Object> valueOptional =
                data.entrySet().stream()
                        .filter(ix -> ix.getKey().equalsIgnoreCase(key) && ix.getValue() != null)
                        .map(ix -> ix.getValue()).findFirst();

        if (valueOptional.isPresent()) {
            return valueOptional.get();
        }

        return null;
    }
}
