package com.red.circle.other.app.listener.gift;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.dto.cmd.RoomIdCmd;
import com.red.circle.component.mq.MessageEventProcess;
import com.red.circle.component.mq.MessageEventProcessDescribe;
import com.red.circle.component.mq.config.RocketMqMessageListener;
import com.red.circle.component.mq.service.Action;
import com.red.circle.component.mq.service.ConsumerMessage;
import com.red.circle.component.mq.service.MessageListener;
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.framework.web.util.ValidatorUtils;
import com.red.circle.live.inner.endpoint.api.LiveHeartbeatApi;
import com.red.circle.live.inner.endpoint.api.LiveMicClientApi;
import com.red.circle.live.inner.model.cmd.LiveHeartbeatAddCmd;
import com.red.circle.mq.business.model.event.game.GameBurstCrystalEvent;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts;
import com.red.circle.mq.business.model.event.gift.GiveGiftConfig;
import com.red.circle.mq.business.model.event.gift.OfflineProcessGiftEvent;
import com.red.circle.mq.business.model.event.gift.OfflineStatisticsGiftEventType;
import com.red.circle.mq.rocket.business.producer.GameMqMessage;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.streams.GiveGiftSink;
import com.red.circle.other.app.command.game.barrage.BarrageGameBaishunExe;
import com.red.circle.other.app.command.game.burstcrystal.GameBurstCrystalScheduleTwoFunQueryExe;
import com.red.circle.other.app.command.game.roompk.GameRoomPkUserCmdExe;
import com.red.circle.other.app.command.game.teampk.GameTeamPkBaseInfoCmdExe;
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon;
import com.red.circle.other.app.dto.clientobject.game.*;
import com.red.circle.other.app.dto.clientobject.gift.SendGiftNotifyCO;
import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.live.LiveHeartbeatCache;
import com.red.circle.other.domain.model.user.ability.RegionConfig;
import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftCountAnchorDailyRunningWater;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftGiveProcessLog;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftGiveRunningWater;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftValue;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
import com.red.circle.other.infra.database.mongo.service.gift.GiftCountAnchorDailyRunningWaterService;
import com.red.circle.other.infra.database.mongo.service.gift.GiftGiveRunningWaterService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.material.GiftSpecialEnum;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardPropsDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

import kotlin.Pair;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

/**
 * @author pengliang on 2022/7/25
 */
@Slf4j
@RocketMqMessageListener(groupId = GiveGiftSink.INPUT, tag = GiveGiftSink.TAG)
@RequiredArgsConstructor
public class GiveGiftsListener implements MessageListener {

    private static final String TAG = "礼物消息";
    private final GameMqMessage gameMqMessage;
    private final GiftMqMessage giftMqMessage;
    private final TeamMemberService teamMemberService;
    private final UserRegionGateway userRegionGateway;
    private final MessageEventProcess messageEventProcess;
    private final GameLuckyGiftCommon gameLuckyGiftCommon;
    private final TeamBillCycleService teamBillCycleService;
    private final TeamPolicyManagerService teamPolicyManagerService;
    private final EnumConfigCacheService enumConfigCacheService;
    private final LiveHeartbeatCacheService liveHeartbeatCacheService;
    private final GiftGiveRunningWaterService giftGiveRunningWaterService;
    private final ImGroupClient imGroupClient;
    private final RoomProfileManagerService roomProfileManagerService;
    private final GameTeamPkBaseInfoCmdExe gameTeamPkBaseInfoCmdExe;
    private final GameRoomPkUserCmdExe gameRoomPkUserCmdExe;
    private final BarrageGameBaishunExe barrageGameBaishunExe;
    private final GameBurstCrystalScheduleTwoFunQueryExe gameBurstCrystalScheduleTwoFunQueryExe;
    private final GiftCountAnchorDailyRunningWaterService giftCountAnchorDailyRunningWaterService;

    @Override
    public Action consume(ConsumerMessage message) {
        return messageEventProcess.consume(
                MessageEventProcessDescribe.builder()
                        .logTag(TAG)
                        .consumeMsgTimeoutMinute(15)
                        .repeatConsumeMinute(15)
                        .repeatConsumeTag("GiveGifts")
                        .checkConsumeTag(GiveGiftSink.TAG)
                        .message(message)
                        .build(),
                GiveAwayGiftBatchEvent.class,
                this::execute);

    }

    private void execute(GiveAwayGiftBatchEvent eventBody) {
        log.warn("礼物消息：{}", eventBody);

        if (!ValidatorUtils.validateFastPass(eventBody)) {
            log.warn("[{}]消息数据异常，必填参数为空：{}", TAG, JacksonUtils.toJson(eventBody));
            return;
        }

        if (!Objects.equals(eventBody.getBagGift(), Boolean.TRUE) && !checkConsumptionReceipt(
                eventBody)) {
            log.warn("[{}]不存在单据信息{}", TAG, JacksonUtils.toJson(eventBody));
            return;
        }

        process(eventBody);
    }

    private void process(GiveAwayGiftBatchEvent eventBody) {
        processGiftSend(eventBody);
        /*processGameBurstCrystal(eventBody);
        try {
            processBarrageGame(eventBody);
        } catch (Exception e) {
            log.error("赠送礼物请求百顺发送特技异常", e);
        }*/
        // 发送IM通知
//        sendIM(eventBody);
    }

    private void processBarrageGame(GiveAwayGiftBatchEvent eventBody) {
        if (eventBody.getGameId() == null) {
            return;
        }
        GameUserEffectsCmd cmd = new GameUserEffectsCmd();
        cmd.setGameId(eventBody.getGameId());
        cmd.setUserId(eventBody.getSendUserId());
        cmd.setGiftNum(eventBody.getQuantity());
        GiveGiftConfig giftConfig = eventBody.getGiftConfig();
        cmd.setGiftPrice(giftConfig.getGiftCandy().intValue());
        Integer effectsId = getEffectsId(eventBody);
        cmd.setEffectsId(effectsId);
        log.info("赠送礼物请求百顺发送特技 入参：{}", cmd);
        barrageGameBaishunExe.userEffects(cmd);
    }

    @NotNull
    private static Integer getEffectsId(GiveAwayGiftBatchEvent eventBody) {
        int effectsId = 1;
        if (eventBody.checkCpGifts()) {
            effectsId = 2;
        }
        if (eventBody.checkLuckyGift()) {
            effectsId = 3;
        }
        if (eventBody.checkExclusive()) {
            effectsId = 4;
        }
        return effectsId;
    }

    private void sendIM(GiveAwayGiftBatchEvent eventBody) {
        RoomIdCmd roomIdCmd = new RoomIdCmd();
        roomIdCmd.setRoomId(eventBody.getRoomId());
        ReqSysOrigin reqSysOrigin = new ReqSysOrigin();
        reqSysOrigin.setOrigin(eventBody.getSysOrigin().getSysOrigin());
        roomIdCmd.setReqSysOrigin(reqSysOrigin);
        SendGiftNotifyCO sendGiftNotify = new SendGiftNotifyCO()
                .setGameIndoorTeamPk(getGameIndoorTeamPk(roomIdCmd))
                .setGameRoomPkUser(getGameRoomPkUser(roomIdCmd))
                .setGameBurstCrystalSchedules(getGameBurstCrystalSchedules(roomIdCmd));
        RoomProfile profileById = roomProfileManagerService.getProfileById(eventBody.getRoomId());
        if (profileById == null) {
            log.warn("{} 发送礼物通知 失败,房间ID = {},未找到房间", eventBody.getSendUserId(), eventBody.getRoomId());
            return;
        }
        imGroupClient.sendCustomMessage(
                profileById.getRoomAccount(),
                CustomGroupMsgBodyCmd.builder()
                        .type(GroupMessageTypeEnum.SEND_GIFT)
                        .data(sendGiftNotify)
                        .build()
        );
        log.warn("{} 发送礼物通知 成功,房间ID = {}", eventBody.getSendUserId(), eventBody.getRoomId());
    }

    private GameIndoorTeamPkCO getGameIndoorTeamPk(RoomIdCmd roomIdCmd) {
        try {
            return gameTeamPkBaseInfoCmdExe.execute(roomIdCmd);
        } catch (Exception e) {
            log.warn("获取房间内团队PK信息失败,{}", e.getMessage());
            return null;
        }
    }

    private GameRoomPkUserCO getGameRoomPkUser(RoomIdCmd roomIdCmd) {
        try {
            return gameRoomPkUserCmdExe.execute(roomIdCmd);
        } catch (Exception e) {
            log.warn("获取房间内PK对战情况失败,{}", e.getMessage());
            return null;
        }
    }

    private List<GameBurstCrystalScheduleCO> getGameBurstCrystalSchedules(RoomIdCmd roomIdCmd) {
        try {
            List<GameBurstCrystalScheduleCO> gameBurstCrystalSchedules = gameBurstCrystalScheduleTwoFunQueryExe.execute(roomIdCmd);
            if (CollectionUtils.isNotEmpty(gameBurstCrystalSchedules)) {
                gameBurstCrystalSchedules.forEach(
                        co -> {
                            getRule(co);
                            getActivityRewardProps(co);
                            getWinnerUserProfiles(co);
                            getTopRewardProps(co);
                            getLuckyBoxRewards(co);
                        }
                );
            }
            return gameBurstCrystalSchedules;
        } catch (Exception e) {
            log.warn("获取爆水晶进度信息失败,{}", e.getMessage());
            return null;
        }
    }

    private static void getRule(GameBurstCrystalScheduleCO co) {
        GameBurstCrystalRuleCO newRule = new GameBurstCrystalRuleCO();
        GameBurstCrystalRuleCO rule = co.getRule();
        if (rule != null) {
            newRule.setLevel(rule.getLevel());
            newRule.setMilestone(rule.getMilestone());
            co.setRule(newRule);
        }
    }

    private static void getLuckyBoxRewards(GameBurstCrystalScheduleCO co) {
        List<ActivityRewardPropsDTO> luckyBoxRewards = co.getLuckyBoxRewards();
        if (CollectionUtils.isNotEmpty(luckyBoxRewards)) {
            luckyBoxRewards.forEach(
                    item -> {
                        ActivityRewardPropsDTO activityRewardProps = new ActivityRewardPropsDTO();
                        activityRewardProps.setId(item.getId());
                        activityRewardProps.setCover(item.getCover());
                        activityRewardProps.setQuantity(item.getQuantity());
                        // 用新对象替换原对象
                        int index = luckyBoxRewards.indexOf(item);
                        luckyBoxRewards.set(index, activityRewardProps);
                    }
            );
        }
    }

    private static void getTopRewardProps(GameBurstCrystalScheduleCO co) {
        List<ActivityRewardPropsDTO> topRewardProps = co.getTopRewardProps();
        if (CollectionUtils.isNotEmpty(topRewardProps)) {
            topRewardProps.forEach(
                    item -> {
                        ActivityRewardPropsDTO activityRewardProps = new ActivityRewardPropsDTO();
                        activityRewardProps.setId(item.getId());
                        activityRewardProps.setCover(item.getCover());
                        // 用新对象替换原对象
                        int index = topRewardProps.indexOf(item);
                        topRewardProps.set(index, activityRewardProps);
                    }
            );
        }
    }

    private static void getWinnerUserProfiles(GameBurstCrystalScheduleCO co) {
        List<GameBurstCrystalWinnerUserProfileCO> winnerUserProfiles = co.getWinnerUserProfiles();
        if (CollectionUtils.isNotEmpty(winnerUserProfiles)) {
            winnerUserProfiles.forEach(
                    item -> {
                        GameBurstCrystalWinnerUserProfileCO winnerUserProfileCO = new GameBurstCrystalWinnerUserProfileCO();
                        winnerUserProfileCO.setRank(item.getRank());
                        UserProfileDTO userProfile = item.getUserProfile();
                        if (userProfile != null) {
                            UserProfileDTO profile = new UserProfileDTO();
                            profile.setId(userProfile.getId());
                            profile.setUserNickname(userProfile.getUserNickname());
                            profile.setUserAvatar(userProfile.getUserAvatar());
                            profile.setAccount(userProfile.getAccount());
                            winnerUserProfileCO.setUserProfile(profile);
                        }
                        // 用新对象替换原对象
                        int index = winnerUserProfiles.indexOf(item);
                        winnerUserProfiles.set(index, winnerUserProfileCO);
                    }
            );
        }
    }

    private static void getActivityRewardProps(GameBurstCrystalScheduleCO co) {
        List<ActivityRewardPropsDTO> activityRewardProps = co.getActivityRewardProps();
        if (CollectionUtils.isNotEmpty(activityRewardProps)) {
            activityRewardProps.forEach(
                    item -> {
                        // 创建一个新对象，只设置id和cover,其他属性设为null
                        ActivityRewardPropsDTO strippedItem = new ActivityRewardPropsDTO();
                        strippedItem.setId(item.getId());
                        strippedItem.setCover(item.getCover());

                        // 用新对象替换原对象
                        int index = activityRewardProps.indexOf(item);
                        activityRewardProps.set(index, strippedItem);
                    }
            );
        }
    }

    private boolean checkConsumptionReceipt(GiveAwayGiftBatchEvent event) {
        if (event.checkGiftTypeEqGold()) {
            return true;
            // newWalletServiceApi.existsId(event.getTrackId());
        }

        if (event.checkGiftTypeEqDiamond()) {
            return true;
        }
        return false;
    }

    private void processGameBurstCrystal(GiveAwayGiftBatchEvent event) {
        log.info("触发游戏mq,{}", event);
        if (Objects.nonNull(event.getRoomId()) && event.checkGiftTypeEqGold()) {
            gameMqMessage.burstCrystal(new GameBurstCrystalEvent()
                    .setSysOrigin(event.getSysOrigin())
                    .setConsumerId(event.getTrackId())
                    .setRoomId(event.getRoomId())
                    .setUserId(event.getSendUserId())
                    .setQuantity(event.countConsumAmountTotal())
                    .setCreateTime(event.getCreateTime())
            );
        }
    }

    private void offlineProcessGiftEvent(OfflineStatisticsGiftEventType eventType,
                                         GiftGiveRunningWater runningWater) {
        giftMqMessage.sendOfflineProcessGiftEvent(new OfflineProcessGiftEvent()
                .setTrackId(runningWater.getTrackId())
                .setType(eventType)
                .setRunningWaterId(runningWater.getId())
        );
    }

    private void processGiftSend(GiveAwayGiftBatchEvent event) {
//        log.warn("cptesttest processGiftSend:{},{}",Objects.isNull(event.getCheckBlessingsGift()),JacksonUtils.toJson(event));
        if (Objects.isNull(event.getCheckBlessingsGift())) {
            event.setCheckBlessingsGift(Boolean.FALSE);
        }

        Pair<GiftGiveRunningWater, Boolean> runningWaterBooleanPair = saveRunningWater(event);
        GiftGiveRunningWater runningWater = runningWaterBooleanPair.getFirst();
        if (!runningWaterBooleanPair.getSecond()) {
            log.warn("保存礼物流水失败:{}", runningWater);
            return;
        }

        if (!SysOriginPlatformEnum.isVoiceSystem(runningWater.getSysOrigin())) {
            return;
        }

        // 不是祝福礼物
        if (Boolean.FALSE.equals(runningWater.getCheckBlessingsGift())) {

            if (event.checkGiftTypeEqGold()) {

                // 主播目标统计相关
                offlineProcessGiftEvent(OfflineStatisticsGiftEventType.GIFT_ANCHOR_COUNT, runningWater);
            }

            // 礼物奖品相关（获得金币等）
            offlineProcessGiftEvent(OfflineStatisticsGiftEventType.GIFT_PRIZE_ACCEPT_EVENT, runningWater);
        }

        // 礼物统计相关
        offlineProcessGiftEvent(OfflineStatisticsGiftEventType.GIFT_STATISTICS, runningWater);

        // 排行榜相关
        offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater);

        // 钻石统计相关
        offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater);
    }

    private Pair<GiftGiveRunningWater,Boolean> saveRunningWater(GiveAwayGiftBatchEvent event) {
        List<Long> anchorUserIds = teamMemberService.listMemberIds(event.allUserId());

        //直播间送礼后的心动值
        toLiveMicHeartbeat(event);

        // 保存礼物流水
        GiftGiveRunningWater runningWater = toGiftGiveRunningWater(event, anchorUserIds);
        Boolean addRunningWater = giftGiveRunningWaterService.add(runningWater);

        Pair<GiftGiveRunningWater, Boolean> result = new Pair<>(runningWater, addRunningWater);
        if (!event.checkGiftTypeEqGold()) {
            return result;
        }

        // count赠送给主播的目标情况
        giftCountAnchorDailyRunningWaterService.incr(
                runningWater.getAcceptUsers().stream()
                        .filter(acceptUser -> Objects.equals(acceptUser.getAnchor(), Boolean.TRUE))
                        .map(acceptUser -> new GiftCountAnchorDailyRunningWater()
                                .setSendUserId(runningWater.getUserId())
                                .setSendAnchor(runningWater.getAnchor())
                                .setAcceptUserId(acceptUser.getAcceptUserId())
                                .setAcceptAnchor(acceptUser.getAnchor())
                                .setQuantity(acceptUser.getTargetAmount().longValue())
                        )
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList())
        );
        return result;
    }

    private GiftGiveRunningWater toGiftGiveRunningWater(GiveAwayGiftBatchEvent event,
                                                        List<Long> anchorUserIds) {
        GiveGiftConfig giftConfig = event.getGiftConfig();

        GiftValue giftValue = getGiftValue(event);

        List<GiftAcceptUser> acceptUsers = getRegionGiftAcceptUser(event, anchorUserIds, giftValue);

        return GiftGiveRunningWater.builder()
            .id(IdWorkerUtils.getId())
            .songId(event.getSongId())
            .refunded(Boolean.FALSE)
            .processed(Boolean.FALSE)
            .trackId(event.getTrackId().toString())
            .originId(Objects.nonNull(event.getRoomId()) ? event.getRoomId().toString() : "OTHER")
            .sysOrigin(event.getSysOrigin().name())
            .requestPlatform(event.getRequestPlatform())
            .userId(event.getSendUserId())
            .anchor(anchorUserIds.contains(event.getSendUserId()))
            .giftId(giftConfig.getId())
            .giftType(giftConfig.getType())
            .giftValue(giftValue)
            .luckyGift(event.checkLuckyGift())
            .checkBlessingsGift(event.getCheckBlessingsGift())
            .logs(List.of(GiftGiveProcessLog.builder()
                .content("流水入库")
                .createTime(TimestampUtils.now())
                .build()))
            .createDateRiyadhNum(event.getCreateDateRiyadhNum())
            .createTime(getCreateTime(event))
            .actualCreateTime(TimestampUtils.now())
            .updateTime(getCreateTime(event))
            .acceptUsers(acceptUsers)
            .build();
    }


    private List<GiftAcceptUser> getRegionGiftAcceptUser(GiveAwayGiftBatchEvent event,
                                                         List<Long> anchorUserIds, GiftValue giftValue) {
        return getRegionGiftAcceptUserRests(event, anchorUserIds, giftValue);
    }


    /**
     * 分区.
     */
    private List<GiftAcceptUser> getRegionGiftAcceptUserRests(GiveAwayGiftBatchEvent event,
                                                             List<Long> anchorUserIds, GiftValue giftValue) {

        log.info("getRegionGiftAcceptUserRests: {} anchorUserIds:{} giftValue:{}", JSON.toJSONString(event), anchorUserIds, JSON.toJSONString(giftValue));

        // 发送人区域与金币比例
        RegionConfig sendRegion = getUserRegion(event.getSendUserId());

        // 幸运礼物比例
        BigDecimal luckyGiftRatio = gameLuckyGiftCommon.getLuckyGiftTargetRatio(event.getSysOrigin().name());

        // 赠送礼物给自己返还金币比例
        BigDecimal sendAssistRatio = event.checkLuckyGift()
                ? luckyGiftRatio
                : getAssistGiftToOwnGoldRatio(sendRegion.getId());

        Map<Long, TeamMember> teamMemberMap = teamMemberService.mapByMemberIds(anchorUserIds);

        return event.getAccepts().stream().map(accept -> {

            // 接受人区域与金币比例
            RegionConfig acceptRegion = getUserRegion(accept.getAcceptUserId());

            // 检查是否为冲突区域
            Boolean isConflicts = !Objects.equals(
                    userRegionGateway.checkBusinessRegionCode(sendRegion.getRegionCode(), acceptRegion.getRegionCode()),
                    Boolean.TRUE
            );

            // 初始化接收金额和目标金额
            BigDecimal acceptAmount = BigDecimal.ZERO;
            BigDecimal targetAmount = BigDecimal.ZERO;
            BigDecimal acceptAssistRatio = BigDecimal.ZERO;

            // 非冲突区域才计算金额
            if (!isConflicts) {
                if (event.checkLuckyGift()) {
                    // 幸运礼物
                    acceptAssistRatio = luckyGiftRatio;
                    targetAmount = giftValue.getGiftValue().multiply(luckyGiftRatio).setScale(0, RoundingMode.DOWN);
                    acceptAmount = targetAmount;

                } else if (event.checkCpGift()){
                    // CP礼物
                    acceptAssistRatio = getAssistCpGiftGoldRatio(acceptRegion.getId());
                    targetAmount = giftValue.getGiftValue().multiply(acceptAssistRatio.multiply(new BigDecimal("10").max(new BigDecimal("0.7")))).setScale(0, RoundingMode.DOWN);
                    acceptAmount = giftValue.getGiftValue().multiply(acceptAssistRatio).setScale(0, RoundingMode.DOWN);

                } else {
                    // 普通礼物
                    acceptAssistRatio = calculateAcceptAssistRatio(event, acceptRegion);
                    BigDecimal effectiveSendRatio = calculateEffectiveSendRatio(event, sendRegion, sendAssistRatio, acceptRegion, accept);

                    acceptAmount = enumConfigCacheService.giftGoldRatioFormula(effectiveSendRatio, giftValue.getGiftValue());
                    targetAmount = giftValue.getGiftValue()
                            .multiply(getAssistGiftTargetRatio(acceptRegion.getId()))
                            .setScale(0, RoundingMode.DOWN);
                }
            }

            // 获得主播的账单ID
            Boolean isAnchor = anchorUserIds.contains(accept.getAcceptUserId());

            return new GiftAcceptUser()
                    .setAcceptUserId(accept.getAcceptUserId())
                    .setAnchor(isAnchor)
                    .setReceiptId(0L)
                    .setRegionIdStr(sendRegion.getId() + "-" + acceptRegion.getId())
                    .setRegion(sendRegion.getRegionName() + "-" + acceptRegion.getRegionName())
                    .setRegionEq(Objects.equals(sendRegion.getId(), acceptRegion.getId()))
                    .setAcceptAmount(acceptAmount)
                    .setTargetAmount(targetAmount)
                    .setPercentage(acceptAssistRatio)
                    .setSelfPercentage(Boolean.TRUE.equals(isConflicts) ? BigDecimal.ZERO : sendAssistRatio)
                    .setCountTargetAmount(Boolean.FALSE)
                    .setCountBillId(getTeamBillId(teamMemberMap.get(accept.getAcceptUserId()), isAnchor))
                    .setSeatIndex(accept.getSeatIndex());

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

    /**
     * 计算接收方助力比例.
     */
    private BigDecimal calculateAcceptAssistRatio(GiveAwayGiftBatchEvent event, RegionConfig acceptRegion) {
        if (userRegionGateway.isOrdinaryUser(event.getSendUserId())) {
            return getAssistGiftToOtherGoldRatioNormal(acceptRegion.getId());
        }
        return getAssistGiftToOtherGoldRatio(acceptRegion.getId());
    }

    /**
     * 计算有效发送比例.
     */
    private BigDecimal calculateEffectiveSendRatio(GiveAwayGiftBatchEvent event, RegionConfig sendRegion,
                                                   BigDecimal sendAssistRatio, RegionConfig acceptRegion,
                                                   GiveAwayGiftRoomAccepts accept) {
        // 送给自己
        if (Objects.equals(event.getSendUserId(), accept.getAcceptUserId())) {
            if (userRegionGateway.isOrdinaryUser(event.getSendUserId())) {
                return getAssistGiftToOwnGoldRatioNormal(sendRegion.getId());
            }
            return sendAssistRatio;
        }
        // 送给别人
        return calculateAcceptAssistRatio(event, acceptRegion);
    }

    /**
     * 代理红包计算分成
     *
     * @param event
     * @param anchorUserIds
     * @param giftValue
     * @return
     */
    private List<GiftAcceptUser> getProxyGiftCalculation(GiveAwayGiftBatchEvent event,
                                                         List<Long> anchorUserIds, GiftValue giftValue) {
        // 发送人区域与金币比例
        RegionConfig sendRegion = getUserRegion(event.getSendUserId());
        BigDecimal sendGifts = getAssistGiftToOwnGoldRatioAgency(sendRegion.getId());

        // 幸运礼物比例
        BigDecimal luckyGiftRatio = sendGifts.multiply(BigDecimal.valueOf(0.1));

        // 赠送礼物给自己返还金币比例
        BigDecimal sendAssistRatio =
                event.checkLuckyGift() ? luckyGiftRatio : sendGifts;

        Map<Long, TeamMember> teamMemberMap = teamMemberService.mapByMemberIds(anchorUserIds);
        return event.getAccepts().stream().map(accept -> {
            // 接受人区域与金币比例
            RegionConfig acceptRegion = getUserRegion(accept.getAcceptUserId());
            BigDecimal acceptAssistRatio =
                    event.checkLuckyGift() ?
                            getAssistGiftToOtherGoldRatioAgency(acceptRegion.getId()).multiply(BigDecimal.valueOf(0.1)) : getAssistGiftToOtherGoldRatioAgency(acceptRegion.getId());
            // 接收金额
            BigDecimal acceptAmount = BigDecimal.ZERO;
            // 接收目标
            BigDecimal targetAmount = BigDecimal.ZERO;

            // 不是冲突区域则不清零目标
            Boolean isConflicts = !Objects.equals(
                    userRegionGateway.checkBusinessRegionCode(sendRegion.getRegionCode(),
                            acceptRegion.getRegionCode()), Boolean.TRUE);
            if (Boolean.FALSE.equals(isConflicts)) {
                // 普通礼物，接受人可获得金额
                acceptAmount = enumConfigCacheService.giftGoldRatioFormula(
                        Objects.equals(event.getSendUserId(), accept.getAcceptUserId())
                                ? sendAssistRatio : acceptAssistRatio, giftValue.getGiftValue());

                // 普通礼物，接受人可获得目标
                targetAmount = giftValue.getGiftValue();
            }
            TeamMember agencyUserId = teamMemberService.getTeamOwn(teamMemberMap.get(accept.getAcceptUserId()).getTeamId());
            return new GiftAcceptUser()
                    .setAcceptUserId(agencyUserId.getMemberId())
                    .setAnchor(Boolean.FALSE)
                    .setReceiptId(0L)
                    .setRegionIdStr(sendRegion.getId() + "-" + acceptRegion.getId())
                    .setRegion(sendRegion.getRegionName() + "-" + acceptRegion.getRegionName())
                    .setRegionEq(Objects.equals(sendRegion.getId(), acceptRegion.getId()))
                    .setAcceptAmount(acceptAmount)
                    .setTargetAmount(targetAmount)
                    .setPercentage(acceptAssistRatio)
                    .setSelfPercentage(Boolean.TRUE.equals(isConflicts) ? BigDecimal.ZERO : sendAssistRatio)
                    .setCountTargetAmount(Boolean.FALSE)
                    .setCountBillId(getTeamBillId(teamMemberMap.get(agencyUserId.getMemberId()), Boolean.FALSE))
                    .setSeatIndex(accept.getSeatIndex());
        }).collect(Collectors.toList());
    }


    private Long getTeamBillId(TeamMember member, Boolean isAnchor) {

        if (Boolean.FALSE.equals(isAnchor) || Objects.isNull(member)) {
            return 0L;
        }

        return Optional.ofNullable(teamBillCycleService.getLatestBillIdByTeamId(member.getTeamId()))
                .orElse(0L);
    }

    /**
     * 赠送礼物给别人返还金币比例.
     */
    private BigDecimal getAssistGiftToOtherGoldRatio(String regionId) {
        return userRegionGateway.getAssistGiftToOtherGoldRatio(regionId);
    }

    /**
     * 赠送礼物给自己返还金币比例.
     */
    private BigDecimal getAssistGiftToOwnGoldRatio(String regionId) {
        return userRegionGateway.getAssistGiftToOwnGoldRatio(regionId);
    }

    /**
     * 赠送礼物给别人返还金币比例.
     */
    private BigDecimal getAssistGiftToOtherGoldRatioAgency(String regionId) {
        return userRegionGateway.getAssistGiftToOtherGoldRatioAgency(regionId);
    }

    /**
     * 赠送礼物给自己返还金币比例.
     */
    private BigDecimal getAssistGiftToOwnGoldRatioAgency(String regionId) {
        return userRegionGateway.getAssistGiftToOwnGoldRatioAgency(regionId);
    }

    /**
     * 赠送礼物给别人返还金币比例-普通用户
     */
    private BigDecimal getAssistGiftToOtherGoldRatioNormal(String regionId) {
      return userRegionGateway.getAssistGiftToOtherGoldRatioNormal(regionId);
    }

    /**
     * 赠送礼物给自己返还金币比例-普通用户
     */
    private BigDecimal getAssistGiftToOwnGoldRatioNormal(String regionId) {
      return userRegionGateway.getAssistGiftToOwnGoldRatioNormal(regionId);
    }

    /**
     * 赠送CP礼物返还金币比例
     */
    private BigDecimal getAssistCpGiftGoldRatio(String regionId) {
        return userRegionGateway.getAssistCpGiftGoldRatio(regionId);
    }

    /**
     * 接收礼物获得目标比例.
     */
    private BigDecimal getAssistGiftTargetRatio(String regionId) {
        return userRegionGateway.getAssistGiftTargetRatio(regionId);
    }

    private RegionConfig getUserRegion(Long userId) {
        return userRegionGateway.getRegionConfigByUserId(userId);
    }

    private GiftValue getGiftValue(GiveAwayGiftBatchEvent event) {

        GiveGiftConfig giftConfig = event.getGiftConfig();

        return new GiftValue()
                .setCurrencyType(giftConfig.getType())
                .setGiftType(getGiftType(event, giftConfig))
                .setUnitPrice(giftConfig.getGiftCandy())
                .setQuantity(event.getQuantity())
                .setUserSize(event.acceptUserSize())
                .setActualAmount(event.countConsumAmountTotal())
                .setGiftValue(event.checkGiftTypeEqGold() ?
                        event.countSingleGiftGolds()
                        : event.countSingleGiftDiamond())
                .setBag(event.getBagGift());
    }

  private static String getGiftType(GiveAwayGiftBatchEvent event, GiveGiftConfig giftConfig) {
      // 周星、周王后或排行榜礼物，使用特殊礼物类型
      if (event.checkWeekStar() || event.checkWeekKingQueen() || isRankingGift(giftConfig.getSpecial())) {
          return giftConfig.getSpecial();
      }

      // 普通礼物，使用默认礼物Tab
      return giftConfig.getGiftTab();
  }

    private static boolean isRankingGift(String special) {
        if (special == null || special.isEmpty()) {
            return false;
        }

        for (RankingActivityType activityType : RankingActivityType.values()) {
            if (special.contains(activityType.name())) {
                return true;
            }
        }

        return false;
    }


  private Timestamp getCreateTime(GiveAwayGiftBatchEvent event) {
        return Objects.nonNull(event.getCreateTime()) ? event.getCreateTime() : TimestampUtils.now();
    }


    private void toLiveMicHeartbeat(GiveAwayGiftBatchEvent event) {
        if(Objects.isNull(event.getRoomId())){
            return;
        }
        //接受用户id
        Set<Long> acceptsUserIds = event.getAccepts().stream()
            .map(GiveAwayGiftRoomAccepts::getAcceptUserId).collect(Collectors.toSet());
        //先获取房间redis值
        List<LiveHeartbeatCache> liveHeartbeatCaches = liveHeartbeatCacheService.listLiveHeartbeat(
            event.getRoomId());
        //组装一下心动值数据 key 用户id
        Map<Long, LiveHeartbeatCache> liveHeartbeatCacheMap = getLiveHeartbeatCacheMap(liveHeartbeatCaches);
        Map<Long, LiveHeartbeatCache> cacheMap = acceptsUserIds.stream().map(userId -> {


          // 礼物比例
          BigDecimal giftRatio = BigDecimal.valueOf(1L);
          if (event.checkLuckyGift()) {
            // 幸运礼物比例
            giftRatio = gameLuckyGiftCommon.getLuckyGiftTargetRatio(event.getSysOrigin().name());
          }
          //总的心动值
          BigDecimal amountTotal = event.countSingleGiftGolds().multiply(giftRatio);

          LiveHeartbeatCache liveHeartbeatCache = new LiveHeartbeatCache();
            liveHeartbeatCache.setId(userId);
            liveHeartbeatCache.setHeartbeatVal(getHeartbeatTotal(amountTotal,
                Objects.nonNull(liveHeartbeatCacheMap.get(userId)) ? liveHeartbeatCacheMap.get(userId)
                    .getHeartbeatVal() : null));
            return liveHeartbeatCache;
        }).collect(Collectors.toMap(LiveHeartbeatCache::getId, v -> v));
        liveHeartbeatCacheMap.putAll(cacheMap);
        if(CollectionUtils.isNotEmpty(liveHeartbeatCacheMap)){
            liveHeartbeatCacheService.create(event.getRoomId(),new ArrayList<>(liveHeartbeatCacheMap.values()));
        }
    }


    private Map<Long,LiveHeartbeatCache> getLiveHeartbeatCacheMap(List<LiveHeartbeatCache> list) {
        return Optional.ofNullable(
                list.stream().collect(
                    Collectors.toMap(LiveHeartbeatCache::getId,v->v)))
            .orElse(new HashMap<>());
    }

    public Long getHeartbeatTotal(BigDecimal newHeartbeat, Long oldHeartbeat) {
        if(Objects.isNull(oldHeartbeat)){
            return newHeartbeat.longValue();
        }
        return newHeartbeat.add(new BigDecimal(oldHeartbeat)).longValue();
    }
}
