package com.red.circle.other.app.inner.service.user.device.impl;

import com.google.common.collect.Lists;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.app.inner.convertor.user.RegisterDeviceInnerConvertor;
import com.red.circle.other.app.inner.convertor.user.UserProfileInnerConvertor;
import com.red.circle.other.app.inner.service.user.device.RegisterDeviceClientService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.rds.entity.user.user.LatestMobileDevice;
import com.red.circle.other.infra.database.rds.service.user.device.ArchiveDeviceService;
import com.red.circle.other.infra.database.rds.service.user.device.DeviceRegisterQuantityService;
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.model.cmd.user.device.ArchiveDeviceQryCmd;
import com.red.circle.other.inner.model.cmd.user.device.DeviceRegisterQuantityQryCmd;
import com.red.circle.other.inner.model.cmd.user.device.PageLatestMobileDeviceQryCmd;
import com.red.circle.other.inner.model.dto.user.UserLatestDeviceAssociatedDTO;
import com.red.circle.other.inner.model.dto.user.UserLatestDeviceDTO;
import com.red.circle.other.inner.model.dto.user.UserLatestMobileDeviceHistoryDTO;
import com.red.circle.other.inner.model.dto.user.device.ArchiveDeviceDTO;
import com.red.circle.other.inner.model.dto.user.device.DeviceRegisterQuantityDTO;
import com.red.circle.other.inner.model.dto.user.device.LatestMobileDeviceDTO;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 注册设备相关服务 实现.
 *
 * @author pengliang on 2023/5/24
 */
@Service
@RequiredArgsConstructor
public class RegisterDeviceClientServiceImpl implements RegisterDeviceClientService {

  private final UserProfileGateway userProfileGateway;
  private final ArchiveDeviceService archiveDeviceService;
  private final UserProfileInnerConvertor userProfileInnerConvertor;
  private final LatestMobileDeviceService latestMobileDeviceService;
  private final RegisterDeviceInnerConvertor registerDeviceInnerConvertor;
  private final DeviceRegisterQuantityService deviceRegisterQuantityService;
  private final TeamMemberService teamMemberService;
  private final EnumConfigCacheService enumConfigCacheService;

  @Override
  public Boolean existsDevice(String deviceNo, String sysOrigin) {
    return archiveDeviceService.existsDevice(deviceNo, sysOrigin);
  }

  @Override
  public void addDeviceNo(String deviceNo, String sysOrigin) {
    archiveDeviceService.addDeviceNo(deviceNo, sysOrigin);
  }


  @Override
  public PageResult<ArchiveDeviceDTO> pageList(ArchiveDeviceQryCmd query) {
    return archiveDeviceService.pageList(query)
        .convert(registerDeviceInnerConvertor::toArchiveDeviceDTO);
  }

  @Override
  public void removeDeviceNo(String deviceNo, String sysOrigin) {
    archiveDeviceService.removeDeviceNo(deviceNo, sysOrigin);
  }

  @Override
  public void delByDeviceNo(Long id) {
    archiveDeviceService.delByDeviceNo(id);
  }

  @Override
  public void uploadLatestDeviceNo(LatestMobileDeviceDTO cmd) {
    if (StringUtils.isBlank(cmd.getSysOrigin())) {
      cmd.setSysOrigin(userProfileGateway.getSysOrigin(cmd.getUserId()));
    }
    latestMobileDeviceService.saveLatestMobileDevice(
        registerDeviceInnerConvertor.toLatestMobileDevice(cmd));
  }

  @Override
  public String getImeiByUserId(Long userId) {
    return latestMobileDeviceService.getImeiByUserId(userId);
  }

  @Override
  public List<Long> listUserIdByImei(String imei, String sysOrigin) {
    return latestMobileDeviceService.listUserIdByImei(imei, sysOrigin);
  }


  @Override
  public List<UserLatestMobileDeviceHistoryDTO> listByImei(String imei, String sysOrigin) {
    return registerDeviceInnerConvertor.toListUserLatestMobileDeviceHistory(
        latestMobileDeviceService.listByImei(imei, sysOrigin));
  }

  @Override
  public List<Long> listLatestAssociationUserIds(Long userId) {
    return listUserIdByImei(getImeiByUserId(userId),
        userProfileGateway.getByUserId(userId).getOriginSys());
  }

  @Override
  public Set<Long> getIpOrImeiUserId(Long userId) {
    return latestMobileDeviceService.getIpOrImeiUserId(userId);
  }


  @Override
  public UserLatestDeviceAssociatedDTO getDeviceByUserId(Long userId) {

    UserProfile userProfile = userProfileGateway.getByUserId(userId);
    if (Objects.isNull(userProfile)) {
      return null;
    }

    LatestMobileDevice latestDevice = latestMobileDeviceService.query()
            .eq(LatestMobileDevice::getUserId, userId)
            .eq(LatestMobileDevice::getSysOrigin, userProfile.getOriginSys())
            .last(PageConstant.LIMIT_ONE).getOne();

    if (Objects.isNull(latestDevice)) {
      return null;
    }

    List<LatestMobileDevice> associatedDevices = latestMobileDeviceService.query()
            .eq(LatestMobileDevice::getImei, latestDevice.getImei())
            .eq(LatestMobileDevice::getSysOrigin, userProfile.getOriginSys())
            .last(PageConstant.DEFAULT_LIMIT)
            .orderByDesc(LatestMobileDevice::getUpdateTime)
            .list();

    List<LatestMobileDevice> associatedIpDevices = latestMobileDeviceService.query()
            .eq(LatestMobileDevice::getIp, latestDevice.getIp())
            .eq(LatestMobileDevice::getSysOrigin, userProfile.getOriginSys())
            .last(PageConstant.DEFAULT_LIMIT)
            .orderByDesc(LatestMobileDevice::getUpdateTime)
            .list();

    if (CollectionUtils.isEmpty(associatedDevices) && CollectionUtils.isNotEmpty(
        associatedIpDevices)) {
      return new UserLatestDeviceAssociatedDTO()
          .setLatestDevice(registerDeviceInnerConvertor.toLatestMobileDeviceDTO(latestDevice))
          .setAssociatedDevice(Lists.newArrayList())
          .setAssociatedIp(Lists.newArrayList());
    }

    Set<Long> userIds = Stream.of(
        Optional.ofNullable(associatedDevices).map(
            associated -> associated.stream().map(LatestMobileDevice::getUserId)
                .collect(Collectors.toList())).orElseGet(Lists::newArrayList),
        Optional.ofNullable(associatedIpDevices)
            .map(associated -> associated.stream().map(LatestMobileDevice::getUserId)
                .collect(Collectors.toList())).orElseGet(Lists::newArrayList)
    ).flatMap(Collection::stream).collect(Collectors.toSet());

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(userIds);

    return new UserLatestDeviceAssociatedDTO()
        .setLatestDevice(registerDeviceInnerConvertor.toLatestMobileDeviceDTO(latestDevice))
        .setAssociatedDevice(mergeUserLatestDevice(latestDevice, associatedDevices, userProfileMap))
        .setAssociatedIp(mergeUserLatestDevice(latestDevice, associatedIpDevices, userProfileMap));
  }

  private List<UserLatestDeviceDTO> mergeUserLatestDevice(LatestMobileDevice latestDevice,
      List<LatestMobileDevice> associatedDevices, Map<Long, UserProfile> userProfileMap) {

    associatedDevices = Optional.ofNullable(associatedDevices).orElse(Lists.newArrayList());

    List<UserLatestDeviceDTO> associatedList = associatedDevices.stream()
        .filter(device -> !Objects.equals(device.getUserId(), latestDevice.getUserId()))
        .map(device -> new UserLatestDeviceDTO()
            .setLatestDevice(registerDeviceInnerConvertor.toLatestMobileDeviceDTO(device))
            .setUserProfile(userProfileInnerConvertor.toUserProfileDTO(
                userProfileMap.get(device.getUserId())))
        )
        .filter(profile -> Objects.nonNull(profile.getUserProfile()))
        .collect(Collectors.toList());

    Set<Long> associatedUserIds = associatedList.stream()
        .filter(associated -> Objects.nonNull(associated.getUserProfile()))
        .map(associated -> associated.getUserProfile().getId())
        .collect(Collectors.toSet());

    // 排除重复的
    if (!Objects.equals(associatedUserIds.size(), associatedList.size())) {
      List<UserLatestDeviceDTO> newAssociatedList = Lists.newArrayList();
      associatedList.forEach(associated -> {
        if (associatedUserIds.contains(associated.getUserProfile().getId())) {
          newAssociatedList.add(associated);
          associatedUserIds.remove(associated.getUserProfile().getId());
        }
      });
      associatedList = newAssociatedList;
    }
    return associatedList;
  }


  @Override
  public PageResult<UserLatestMobileDeviceHistoryDTO> pageLatestMobileDevice(
      PageLatestMobileDeviceQryCmd query) {
    return latestMobileDeviceService.pageLatestMobileDevice(query)
        .convert(registerDeviceInnerConvertor::toPageLatestMobileDeviceHistoryDTO);
  }

  /**
   * 获取注册设备数量映射.
   */
  @Override
  public Map<String, DeviceRegisterQuantityDTO> mapDeviceRegisterQuantity(
      DeviceRegisterQuantityQryCmd cmd) {
    return registerDeviceInnerConvertor.toMapDeviceRegisterQuantityDTO(
        deviceRegisterQuantityService.mapDeviceRegisterQuantity(cmd.getDeviceNos(),
            cmd.getSysOrigin()));
  }


  @Override
  public Map<Long, String> mapImeiByIds(Set<Long> userIds) {
    return latestMobileDeviceService.mapImeiByIds(userIds);
  }

  @Override
  public String getDeviceFingerprintByUserId(Long userId) {
    return latestMobileDeviceService.getDeviceFingerprintByUserId(userId);
  }

  @Override
  public Boolean checkDeviceFingerprintHasAnchor(Long userId) {
    if (userId == null) {
      return false;
    }

    UserProfile userProfile = userProfileGateway.getByUserId(userId);
    if (userProfile == null) {
      return false;
    }

    String fingerprint = latestMobileDeviceService.getDeviceFingerprintByUserId(userId);
    if (StringUtils.isBlank(fingerprint)) {
      return false;
    }

    // 跳过白名单用户
    String whitelistConfig = enumConfigCacheService.getValue(
        EnumConfigKey.DEVICE_REGISTER_WHITELIST, userProfile.getOriginSys());
    if (StringUtils.isNotBlank(whitelistConfig) && whitelistConfig.contains(String.valueOf(userProfile.getAccount()))) {
      return false;
    }

    List<Long> userIds = latestMobileDeviceService.listUserIdsByDeviceFingerprint(
        fingerprint, userProfile.getOriginSys());

    if (CollectionUtils.isEmpty(userIds)) {
      return false;
    }

    for (Long otherUserId : userIds) {
      if (Objects.equals(otherUserId, userId)) {
        continue;
      }
      
      // 判断是否为主播：检查用户是否为团队成员
      TeamMember teamMember =
          teamMemberService.getByMemberId(otherUserId);
      if (teamMember != null) {
        return true;
      }
    }

    return false;
  }
}
