package com.red.circle.live.inner.service.impl;

import com.red.circle.component.redis.service.RedisService;
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.asserts.ResponseAssert;
import com.red.circle.live.domain.live.LiveHeartbeat;
import com.red.circle.live.domain.live.LiveMicUser;
import com.red.circle.live.domain.live.LiveMicrophone;
import com.red.circle.live.domain.live.dto.LiveHeartbeatDTO;
import com.red.circle.live.domain.live.dto.LiveHeartbeatNoticeDTO;
import com.red.circle.live.domain.live.dto.LiveMicrophoneDTO;
import com.red.circle.live.domain.live.dto.LiveMicrophoneNoticeDTO;
import com.red.circle.live.infra.convertor.LiveMicInfraConvertor;
import com.red.circle.live.infra.database.cache.service.LiveHeartbeatCacheService;
import com.red.circle.live.infra.database.cache.service.LiveMicCacheService;
import com.red.circle.live.infra.database.mongo.entity.LiveMicSetting;
import com.red.circle.live.infra.database.mongo.entity.LiveMicSettingSeat;
import com.red.circle.live.infra.database.mongo.service.LiveMicSettingService;
import com.red.circle.live.inner.model.cmd.LiveHeartbeatAddCmd;
import com.red.circle.live.inner.service.LiveHeartbeatClientService;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.other.inner.endpoint.live.RoomManagerClient;
import com.red.circle.other.inner.model.dto.live.RoomProfileDTO;
import com.red.circle.other.inner.model.dto.live.RoomProfileManagerDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.NumUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.thread.ThreadPoolManager;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 直播间用户心动值
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LiveHeartbeatClientServiceImpl implements LiveHeartbeatClientService {

  private final RedisService redisService;
  private final ImGroupClient imGroupClient;
  private final RoomManagerClient roomManagerClient;
  private final LiveMicCacheService liveMicCacheService;
  private final LiveMicSettingService liveMicSettingService;
  private final LiveHeartbeatCacheService liveHeartbeatCacheService;

  @Override
  public void create(LiveHeartbeatAddCmd qryCmd) {
    //组装数据 把数据放入redis
    if(Objects.isNull(qryCmd.getRoomId())){
      return;
    }
    //查询这个房间之前的数据
    List<LiveHeartbeat> liveHeartbeats = liveHeartbeatCacheService.listLiveHeartbeat(qryCmd.getRoomId());
    Map<Long, LiveHeartbeat> userHeartbeatMap=getUserHeartbeatMap(liveHeartbeats);
    Map<Long, LiveHeartbeat> newUserHeartbeatMap = qryCmd.getUserIds().stream().map(userId -> {
      return new LiveHeartbeat()
          .setId(userId)
          .setHeartbeatVal(getHeartbeatTotal(qryCmd.getHeartbeatTotal(),
              Objects.nonNull(userHeartbeatMap.get(userId)) ? userHeartbeatMap.get(userId)
                  .getHeartbeatVal() : null));
    }).collect(Collectors.toMap(LiveHeartbeat::getId, v -> v));
    userHeartbeatMap.putAll(newUserHeartbeatMap);
    liveHeartbeatCacheService.create(qryCmd.getRoomId(), new ArrayList<>(userHeartbeatMap.values()));

    //通知屏蔽掉 不用后端通知
//    RoomProfileDTO roomProfile = roomManagerClient.getById(qryCmd.getRoomId()).getBody();
//    micUserChangeNotice(roomProfile);
  }



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


  /**
   * 发送通知
   */
  private void micUserChangeNotice(RoomProfileDTO roomProfile) {
     micUserChangeNotice(roomProfile, listLiveMicrophone(roomProfile.getId()));
  }

  private LiveHeartbeatNoticeDTO micUserChangeNotice(RoomProfileDTO roomProfile,
      List<LiveHeartbeatDTO> heartbeatDTOList) {
    LiveHeartbeatNoticeDTO noticeRes = new LiveHeartbeatNoticeDTO()
        .setTimeId(IdWorkerUtils.getIdStr())
        .setList(heartbeatDTOList);
    ThreadPoolManager.getInstance().execute(() ->{
      imGroupClient.sendCustomMessage(
          roomProfile.getRoomAccount(),
          CustomGroupMsgBodyCmd.builder()
              .type(GroupMessageTypeEnum.MIC_HEARTBEAT)
              .data(noticeRes)
              .build()
      );
    });
    return noticeRes;
  }

  /**
   * 获当前， 在线取麦克风列表信息.
   */
  public List<LiveHeartbeatDTO> listLiveMicrophone(Long roomId) {

    List<LiveMicSettingSeat> liveMicSettingSeats = initMicSetting(roomId);
    if (CollectionUtils.isEmpty(liveMicSettingSeats)) {
      return CollectionUtils.newArrayList();
    }
    Map<Integer, LiveHeartbeatDTO> liveMicrophoneMap = mapLiveMicrophone(roomId);
    return liveMicSettingSeats.stream().map(seat -> new LiveHeartbeatDTO()
        .setRoomId(roomId)
        .setId(Objects.isNull(liveMicrophoneMap.get(seat.getMicIndex()))?null:liveMicrophoneMap.get(seat.getMicIndex()).getId())
        .setMicIndex(seat.getMicIndex())
        .setHeartbeatVal(Objects.isNull(liveMicrophoneMap.get(seat.getMicIndex()))?null:liveMicrophoneMap.get(seat.getMicIndex()).getHeartbeatVal())
    ).toList();
  }


  /**
   * 兼容处理： 旧版本没有麦克风管理的房间.
   */
  private List<LiveMicSettingSeat> initMicSetting(Long roomId) {
    List<LiveMicSettingSeat> liveMicSettingSeats = liveMicSettingService.listMicSeat(roomId);

    if (CollectionUtils.isNotEmpty(liveMicSettingSeats)) {
      return liveMicSettingSeats;
    }
    RoomProfileManagerDTO roomProfileManagerDTO = roomManagerClient.getRoomProfileManagerById(
        roomId).getBody();
    ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, roomProfileManagerDTO);
    int micSize = roomProfileManagerDTO.getSetting().getMikeSize();
    if (micSize <= 0) {
      return CollectionUtils.newArrayList();
    }

    return Optional.ofNullable(init(roomId, micSize))
        .map(LiveMicSetting::getMicSeats)
        .orElseGet(CollectionUtils::newArrayList);
  }


  private Map<Integer, LiveHeartbeatDTO> mapLiveMicrophone(Long roomId) {
    List<LiveMicrophone> liveMicrophones = liveMicCacheService.listLiveMicrophone(roomId);
    //获取房间用户心动值
    Map<Long, Long> liveHeartbeatMap = getLiveHeartbeatMap(liveHeartbeatCacheService.listLiveHeartbeat(roomId));
    if (CollectionUtils.isEmpty(liveMicrophones)) {
      return CollectionUtils.newHashMap();
    }

    return liveMicrophones.stream()
        .map(liveMicrophone -> {
          Long userId = liveMicrophone.getUser().getId();
          LiveHeartbeatDTO liveHeartbeatDTO = new LiveHeartbeatDTO();
          liveHeartbeatDTO.setId(userId);
          liveHeartbeatDTO.setMicIndex(liveMicrophone.getMicIndex());
          liveHeartbeatDTO.setHeartbeatVal(Objects.nonNull(liveHeartbeatMap.get(userId))? NumUtils.formatLong(liveHeartbeatMap.get(userId)):"0");
          return liveHeartbeatDTO;
        })
        .collect(Collectors.toMap(LiveHeartbeatDTO::getMicIndex, v -> v));
  }

  /**
   * 初始化麦克风.
   */
  private LiveMicSetting init(Long roomId, Integer micSize) {
    String lockKey = "CMIC:" + roomId;
    if (!redisService.lock(lockKey, 60)) {
      return null;
    }
    try {
      if (liveMicSettingService.exists(roomId)) {
        return null;
      }
      return liveMicSettingService.updateMicSize(roomId, micSize);
    } finally {
      redisService.unlock(lockKey);
    }
  }


  public Map<Long,Long> getLiveHeartbeatMap(List<LiveHeartbeat> list){
    if (CollectionUtils.isEmpty(list)) {
      return new HashMap<>();
    }
    return list.stream().collect(Collectors.toMap(LiveHeartbeat::getId,LiveHeartbeat::getHeartbeatVal));
  }

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