package com.red.circle.other.infra.database.rds.service.user.device.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.infra.database.rds.dao.user.device.LatestMobileDeviceDAO;
import com.red.circle.other.infra.database.rds.entity.user.user.LatestMobileDevice;
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
import com.red.circle.other.inner.model.cmd.user.device.PageLatestMobileDeviceQryCmd;
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 org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户最新设备信息 服务实现类.
 * </p>
 *
 * @author pengliang
 * @since 2021-03-27
 */
@Service
public class LatestMobileDeviceServiceImpl extends
    BaseServiceImpl<LatestMobileDeviceDAO, LatestMobileDevice> implements
    LatestMobileDeviceService {

  @Override
  public String getImeiByUserId(Long userId) {
    return Optional.ofNullable(query().select(LatestMobileDevice::getImei)
            .eq(LatestMobileDevice::getUserId, userId)
            .last(PageConstant.LIMIT_ONE)
            .getOne())
        .map(LatestMobileDevice::getImei)
        .orElse(null);
  }

  @Override
  public LatestMobileDevice getByUserId(Long userId) {

    return query()
        .eq(LatestMobileDevice::getUserId, userId)
        .orderByDesc(LatestMobileDevice::getCreateTime)
        .getOne();
  }

  @Override
  public List<LatestMobileDevice> getByUserId(Set<Long> userIds) {
    return query()
            .in(LatestMobileDevice::getUserId, userIds)
            .orderByDesc(LatestMobileDevice::getCreateTime).list();
  }

  @Override
  public List<Long> listUserIdByImei(String imei, String sysOrigin) {
    if (StringUtils.isBlank(imei)) {
      return CollectionUtils.newArrayList();
    }
    return Optional.ofNullable(query().select(LatestMobileDevice::getUserId)
            .eq(LatestMobileDevice::getImei, imei)
            .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
            .last(PageConstant.MAX_LIMIT)
            .list())
        .map(device -> device.stream().map(LatestMobileDevice::getUserId).distinct()
            .collect(Collectors.toList()))
        .orElseGet(CollectionUtils::newArrayList);
  }


  @Override
  public List<LatestMobileDevice> listByImei(String imei, String sysOrigin) {
    if (StringUtils.isBlank(imei)) {
      return Lists.newArrayList();
    }
    return query()
        .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
        .eq(LatestMobileDevice::getImei, imei)
        .last(PageConstant.MAX_LIMIT)
        .list();
  }

  @Override
  public void saveLatestMobileDevice(LatestMobileDevice latestMobileDevice) {
    delete().eq(LatestMobileDevice::getUserId, latestMobileDevice.getUserId())
        .last(PageConstant.LIMIT_ONE).execute();
    try {
      save(latestMobileDevice);
    } catch (DuplicateKeyException ignore) {
      // ignore
    }
  }

  @Override
  public String getLatestLanguage(Long userId) {

    return Optional.ofNullable(query().eq(LatestMobileDevice::getUserId, userId)
            .last(PageConstant.LIMIT_ONE)
            .orderByDesc(LatestMobileDevice::getCreateTime)
            .getOne()).map(LatestMobileDevice::getLanguage)
        .orElse(null);
  }

  @Override
  public Set<Long> getIpOrImeiUserId(Long userId) {

    LatestMobileDevice mobileDevice = query()
        .eq(LatestMobileDevice::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (Objects.isNull(mobileDevice)) {
      return CollectionUtils.newHashSet();
    }

    Set<Long> userIds = Optional.ofNullable(query()
            .select(LatestMobileDevice::getUserId)
            .eq(LatestMobileDevice::getImei, mobileDevice.getImei())
            .last(PageConstant.formatLimit(500))
            .list())
        .map(latestMobileDevices -> latestMobileDevices.stream().map(LatestMobileDevice::getUserId)
            .collect(Collectors.toSet()))
        .orElse(CollectionUtils.newHashSet());

    userIds.addAll(Optional.ofNullable(query()
            .select(LatestMobileDevice::getUserId)
            .eq(LatestMobileDevice::getIp, mobileDevice.getIp())
            .last(PageConstant.formatLimit(500))
            .list())
        .map(latestMobileDevices -> latestMobileDevices.stream().map(LatestMobileDevice::getUserId)
            .collect(Collectors.toSet()))
        .orElse(CollectionUtils.newHashSet()));

    return userIds;

  }

  @Override
  public Set<String> getImeiByUserIp(Long userId) {

    LatestMobileDevice mobileDevice = query()
        .eq(LatestMobileDevice::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (Objects.isNull(mobileDevice)) {
      return CollectionUtils.newHashSet();
    }

    Set<String> imeiList = Optional.ofNullable(query()
            .select(LatestMobileDevice::getImei)
            .eq(LatestMobileDevice::getIp, mobileDevice.getIp())
            .last(PageConstant.formatLimit(500))
            .list())
        .map(latestMobileDevices -> latestMobileDevices.stream().map(LatestMobileDevice::getImei)
            .collect(Collectors.toSet()))
        .orElse(CollectionUtils.newHashSet());
    imeiList.add(mobileDevice.getImei());
    return imeiList;
  }

  @Override
  public Boolean isFirstLogin(Long userId, String imei) {
    if (Objects.isNull(userId)) {
      return Boolean.TRUE;
    }
    return Optional.ofNullable(query()
            .select(LatestMobileDevice::getId)
            .eq(LatestMobileDevice::getImei, imei)
            .eq(LatestMobileDevice::getUserId, userId)
            .last(PageConstant.LIMIT_ONE)
            .getOne())
        .map(latestMobileDevice -> Objects.isNull(latestMobileDevice.getId()))
        .orElse(Boolean.TRUE);
  }

  @Override
  public PageResult<LatestMobileDevice> pageLatestMobileDevice(
      PageLatestMobileDeviceQryCmd query) {
    return query()
        .eq(StringUtils.isNotBlank(query.getSysOrigin()), LatestMobileDevice::getSysOrigin,
            query.getSysOrigin())
        .eq(Objects.nonNull(query.getUserId()), LatestMobileDevice::getUserId,
            query.getUserId())
        .eq(StringUtils.isNotBlank(query.getImei()), LatestMobileDevice::getImei,
            query.getImei())
        .ge(Objects.nonNull(query.startTimeToLocalDateTime()), LatestMobileDevice::getCreateTime,
            query.startTimeToLocalDateTime())
        .le(Objects.nonNull(query.endTimeToLocalDateTime()), LatestMobileDevice::getCreateTime,
            query.endTimeToLocalDateTime())
        .orderByDesc(LatestMobileDevice::getCreateTime)
        .page(query.getPageQuery());
  }

  @Override
  public Map<Long, String> mapImeiByIds(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return Maps.newHashMap();
    }

    List<LatestMobileDevice> list = query()
        .select(LatestMobileDevice::getImei, LatestMobileDevice::getUserId)
        .in(LatestMobileDevice::getUserId, userIds)
        .orderByAsc(LatestMobileDevice::getCreateTime)
        .list();
    if (CollectionUtils.isEmpty(list)) {
      return Maps.newHashMap();
    }

    Map<Long, String> map = Maps.newHashMap();
    for (LatestMobileDevice device : list) {
      map.put(device.getUserId(), device.getImei());
    }

    return map;
  }

  @Override
  public Long getCountByIp(String ip,String sysOrigin) {

    return query()
            .eq(LatestMobileDevice::getIp, ip)
            .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
            .count();
  }

  @Override
  public Long getCountByImei(String imei,String sysOrigin) {
    return query()
            .eq(LatestMobileDevice::getImei, imei)
            .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
            .count();
  }

  @Override
  public Long getCountByImeiAndIp(String imei, String ip, String sysOrigin) {
    return query()
            .eq(LatestMobileDevice::getImei, imei)
            .eq(LatestMobileDevice::getIp, ip)
            .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
            .count();

  }

  @Override
  public Set<Long> getImeiUserId(Long userId) {

    LatestMobileDevice mobileDevice = query()
            .eq(LatestMobileDevice::getUserId, userId)
            .last(PageConstant.LIMIT_ONE)
            .getOne();

    if (Objects.isNull(mobileDevice)) {
      return Sets.newHashSet();
    }

    Set<Long> userIds = Optional.ofNullable(query()
                    .select(LatestMobileDevice::getUserId)
                    .eq(LatestMobileDevice::getSysOrigin, mobileDevice.getSysOrigin())
                    .eq(LatestMobileDevice::getImei, mobileDevice.getImei())
                    .last(PageConstant.formatLimit(500))
                    .list())
            .map(latestMobileDevices -> latestMobileDevices.stream().map(LatestMobileDevice::getUserId)
                    .collect(Collectors.toSet()))
            .orElse(Sets.newHashSet());
    return userIds;

  }

  @Override
  public String getDeviceFingerprintByUserId(Long userId) {
    if (userId == null) {
      return "";
    }

    LatestMobileDevice device = query()
        .eq(LatestMobileDevice::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (device == null) {
      return "";
    }

    return buildDeviceFingerprint(
        device.getRequestClient(),
        device.getPhoneModel(),
        device.getIp()
    );
  }

  /**
   * 构建设备指纹
   */
  private String buildDeviceFingerprint(String requestClient,
                                        String phoneModel, String ip) {
    if (StringUtils.isBlank(requestClient)
        || StringUtils.isBlank(phoneModel)
        || StringUtils.isBlank(ip)) {
      return "";
    }

    return requestClient + "|" + phoneModel + "|" + ip;
  }

  @Override
  public List<Long> listUserIdsByDeviceFingerprint(String fingerprint, String sysOrigin) {
    if (StringUtils.isBlank(fingerprint) || StringUtils.isBlank(sysOrigin)) {
      return CollectionUtils.newArrayList();
    }

    String[] parts = fingerprint.split("\\|");
    if (parts.length != 3) {
      return CollectionUtils.newArrayList();
    }

    String requestClient = parts[0];
    String phoneModel = parts[1];
    String ip = parts[2];

    return Optional.ofNullable(query()
            .select(LatestMobileDevice::getUserId)
            .eq(LatestMobileDevice::getRequestClient, requestClient)
            .eq(LatestMobileDevice::getPhoneModel, phoneModel)
            .eq(LatestMobileDevice::getIp, ip)
            .eq(LatestMobileDevice::getSysOrigin, sysOrigin)
            .last(PageConstant.formatLimit(500))
            .list())
        .map(devices -> devices.stream()
            .map(LatestMobileDevice::getUserId)
            .distinct()
            .collect(Collectors.toList()))
        .orElseGet(CollectionUtils::newArrayList);
  }

}
