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


import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.google.common.collect.Maps;
import com.red.circle.common.business.enums.AccountStatusEnum;
import com.red.circle.common.business.enums.GenderEnum;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.entity.TimestampBaseEntity;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.convertor.user.SysUserBaseInfoConvertor;
import com.red.circle.other.infra.database.rds.dao.user.user.BaseInfoDAO;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicReport;
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
import com.red.circle.other.inner.model.cmd.count.SysOriginUserQryCmd;
import com.red.circle.other.inner.model.cmd.user.UserBaseInfoPageQryCmd;
import com.red.circle.other.inner.model.dto.pet.UserBaseInfoDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.tuple.ImmutableKeyValuePair;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户基础信息表 服务实现类.
 * </p>
 *
 * @author pengliang
 * @since 2019-08-26
 */
@RequiredArgsConstructor
@Service
public class BaseInfoServiceImpl extends BaseServiceImpl<BaseInfoDAO, BaseInfo> implements
    BaseInfoService {

  private final BaseInfoDAO baseInfoDAO;
  private final SysUserBaseInfoConvertor userBaseInfoConvertor;

  @Override
  public List<BaseInfo> listVest(int size) {
    return query()
        .eq(BaseInfo::getUserType, 1)
        .last(PageConstant.formatLimit(size))
        .list();
  }

  @Override
  public List<BaseInfo> listUser(SysOriginUserQryCmd qryCmd) {
    //DAY,MONTH,YEAR
    return query()
            .eq(StringUtils.isNotBlank(qryCmd.getCountryCode()), BaseInfo::getCountryCode, qryCmd.getCountryCode())
            .ge(StringUtils.isNotBlank(qryCmd.getDate()), BaseInfo::getCreateTime,
                    TimestampUtils.convert(getStartDate(qryCmd.getDate(), qryCmd.getDateType())))
            .le(StringUtils.isNotBlank(qryCmd.getDate()), BaseInfo::getCreateTime,
                    TimestampUtils.convert(getEndDate(qryCmd.getDate(), qryCmd.getDateType())))
            .list();
  }

  /**
   * 获取开始时间
   *
   * @return Date
   */
  public Date getStartDate(String date, String dateType) {
      try {
        SimpleDateFormat formatterYear = switch (dateType) {
            case "YEAR" -> new SimpleDateFormat("yyyy");
            case "MONTH" -> new SimpleDateFormat("yyyyMM");
            case "DAY" -> new SimpleDateFormat("yyyyMMdd");
            default -> null;
        };
        assert formatterYear != null;
        log.warn("start date {}" + formatterYear.parse(date));
        return formatterYear.parse(date);
      } catch (ParseException e) {
        return new Date();
      }
  }

  /**
   * 获取开始时间
   *
   * @return Date
   */
  public Date getEndDate(String date, String dateType) {
    try {
      SimpleDateFormat formatterYear = switch (dateType) {
        case "YEAR" -> new SimpleDateFormat("yyyy");
        case "MONTH" -> new SimpleDateFormat("yyyyMM");
        case "DAY" -> new SimpleDateFormat("yyyyMMdd");
        default -> null;
      };
      assert formatterYear != null;
      Date newDate = formatterYear.parse(date);
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(newDate);
      if (dateType.equals("YEAR")) {
        calendar.add(Calendar.YEAR, 1);
      }else if (dateType.equals("MONTH")) {
        calendar.add(Calendar.MONTH, 1);
      }else {
        calendar.add(Calendar.DATE, 1);
      }
      log.warn("end date {}" + calendar.getTime());
      return calendar.getTime();
    } catch (ParseException e) {
      return new Date();
    }
  }

  @Override
  public String getSysOrigin(Long userId) {
    return Optional.ofNullable(query().select(BaseInfo::getOriginSys).eq(BaseInfo::getId, userId)
            .last(PageConstant.LIMIT_ONE).getOne())
        .map(BaseInfo::getOriginSys)
        .orElse(null);
  }

  @Override
  public Long maxUserAccount() {
    return baseInfoDAO.maxUserAccount();
  }

  @Override
  public Long maxUserAccount(Long start, Long end) {
    return baseInfoDAO.maxUserAccountRange(start, end);
  }

  private boolean updateColumn(Long userId, SFunction<BaseInfo, ?> column, Object val) {
    return update()
        .set(column, val)
        .set(BaseInfo::getUpdateTime, LocalDateTime.now())
        .eq(BaseInfo::getId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }


  @Override
  public PageResult<BaseInfo> pageQuery(UserBaseInfoPageQryCmd query) {
    return this.query()
            .eq(Objects.nonNull(query.getDel()), BaseInfo::getDel, query.getDel())
            .in(Objects.nonNull(query.getUserIds()) && !query.getUserIds().isEmpty(), BaseInfo::getId, query.getUserIds())
            .eq(Objects.nonNull(query.getUserSex()), BaseInfo::getUserSex, query.getUserSex())
            .eq(Objects.nonNull(query.getUserType()), BaseInfo::getUserType, query.getUserType())
            .ge(Objects.nonNull(query.getStartCreateDate()), TimestampBaseEntity::getCreateTime, query.getStartCreateDate())
            .le(Objects.nonNull(query.getEndCreateDate()), TimestampBaseEntity::getCreateTime, query.getEndCreateDate())
            .eq(StringUtils.isNotBlank(query.getCountryCode()), BaseInfo::getCountryCode, query.getCountryCode())
            .inSql(StringUtils.isNotBlank(query.getAuthType()), BaseInfo::getId,
                    "SELECT uri.user_id FROM user_register_info uri WHERE uri.auth_type = '" + query.getAuthType() + "'")
            .inSql(StringUtils.isNotBlank(query.getOriginPlatform()), BaseInfo::getId,
                    "SELECT uri.user_id FROM user_register_info uri WHERE uri.origin_platform = '" + query.getOriginPlatform() + "'")
            .in(Objects.nonNull(query.getSysOrigins()) && !query.getSysOrigins().isEmpty(), BaseInfo::getOriginSys, query.getSysOrigins())
            .inSql(StringUtils.isNotBlank(query.getDeviceId()), BaseInfo::getId,
                    "SELECT ulmd.user_id FROM user_latest_mobile_device ulmd WHERE ulmd.imei = '" + query.getDeviceId() + "'")
            .orderByDesc(BaseInfo::getId)
            .page(query.getPageQuery());
  }

  @Override
  public boolean updateAvatar(Long userId, String avatar) {
    return updateColumn(userId, BaseInfo::getUserAvatar, avatar);
  }

  @Override
  public boolean updateAvatar(Collection<Long> userIds, String avatar) {
    if (CollectionUtils.isEmpty(userIds)) {
      return false;
    }
    return update().set(BaseInfo::getUserAvatar, avatar)
        .in(BaseInfo::getId, userIds)
        .execute();
  }

  @Override
  public boolean updateNickname(Long userId, String nickname) {
    return updateColumn(userId, BaseInfo::getUserNickname, nickname);
  }

  @Override
  public boolean updateNicknameBatch(
      Collection<ImmutableKeyValuePair<Long, String>> keyValuePairs) {
    if (CollectionUtils.isEmpty(keyValuePairs)) {
      return false;
    }

    return updateBatchById(keyValuePairs.stream().map(pair -> new BaseInfo()
            .setId(pair.getKey())
            .setUserNickname(pair.getValue()))
        .toList());
  }

  @Override
  public boolean removeAccountArchive(Long userId) {
    return update()
        .set(BaseInfo::getAccountStatus, AccountStatusEnum.NORMAL)
        .set(BaseInfo::getFreezingTime, LocalDateTime.now())
        .eq(BaseInfo::getId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public boolean removeAccountFreeze(Long userId) {
    return update()
        .set(BaseInfo::getAccountStatus, AccountStatusEnum.NORMAL)
        .set(BaseInfo::getFreezingTime, LocalDateTime.now())
        .eq(BaseInfo::getId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public String getNicknameByAccount(Long account) {
    return Optional
        .ofNullable(
            query().select(BaseInfo::getUserNickname).eq(BaseInfo::getAccount, account).getOne())
        .map(BaseInfo::getUserNickname)
        .orElse(null);
  }

  @Override
  public Map<Long, BaseInfo> mapBaseInfo(Collection<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newHashMap();
    }
    return Optional.ofNullable(
            query().in(BaseInfo::getId, userIds)
                .last(PageConstant.formatLimit(userIds.size()))
                .list()
        ).map(baseInfo -> baseInfo.stream().collect(Collectors.toMap(BaseInfo::getId, v -> v)))
        .orElse(CollectionUtils.newHashMap());
  }

  @Override
  public boolean updateAccountStatus(Long userId, AccountStatusEnum accountStatus) {
    return update()
        .set(BaseInfo::getAccountStatus, accountStatus)
        .set(Objects.equals(accountStatus, AccountStatusEnum.FREEZE), BaseInfo::getFreezingTime,
            TimestampUtils.nowPlusDays(3))
        .set(Objects.equals(accountStatus, AccountStatusEnum.NORMAL), BaseInfo::getFreezingTime,
                    TimestampUtils.now())
        .set(Objects.equals(accountStatus, AccountStatusEnum.ARCHIVE), BaseInfo::getFreezingTime,
            TimestampUtils.nowPlusDays(365))
        .set(Objects.equals(accountStatus, AccountStatusEnum.ARCHIVE_DEVICE), BaseInfo::getFreezingTime,
                TimestampUtils.nowPlusDays(365))
        .eq(BaseInfo::getId, userId)
        .execute();
  }


  @Override
  public boolean updateAccountStatusDays(Long userId, AccountStatusEnum accountStatus, Integer days) {
    return update()
        .set(BaseInfo::getAccountStatus, accountStatus)
        .set(Objects.equals(accountStatus, AccountStatusEnum.ARCHIVE), BaseInfo::getFreezingTime, TimestampUtils.nowPlusDays(days))
        .eq(BaseInfo::getId, userId)
        .execute();
  }

  @Override
  public boolean deleteUser(Long userId) {
    return update()
        .set(BaseInfo::getDel, Boolean.TRUE)
        .set(BaseInfo::getUpdateTime, LocalDateTime.now())
        .eq(BaseInfo::getId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public List<BaseInfo> listByUserId(Collection<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newArrayList();
    }
    return query().in(BaseInfo::getId, userIds).last(PageConstant.formatLimit(userIds.size()))
        .list();
  }

  @Override
  public List<BaseInfo> listByAccount(Collection<String> accounts) {
    if (CollectionUtils.isEmpty(accounts)) {
      return CollectionUtils.newArrayList();
    }
    return query().in(BaseInfo::getAccount, accounts)
        .last(PageConstant.formatLimit(accounts.size()))
        .list();
  }

  @Override
  public BaseInfo getByAccount(String account) {
    return query().eq(BaseInfo::getAccount, account).last(PageConstant.LIMIT_ONE).getOne();
  }

  @Override
  public BaseInfo getByAccount(String account,String sysOrigin) {
    return query().eq(BaseInfo::getAccount, account).eq(BaseInfo::getOriginSys, sysOrigin).last(PageConstant.LIMIT_ONE).getOne();
  }

  @Override
  public BaseInfo getByAccountAndLongCacooTrueAndFemale(String account) {
    return query().eq(BaseInfo::getAccount, account)
        //.eq(BaseInfo::getLoginCacoo, Boolean.TRUE)
        .eq(BaseInfo::getUserSex, GenderEnum.FEMALE.getValue())
        .eq(BaseInfo::getDel, Boolean.FALSE)
        .last(PageConstant.LIMIT_ONE).getOne();
  }

  @Override
  public List<BaseInfo> listUserByLastId(Long lastId) {
    return query()
        .eq(BaseInfo::getUserType, 0)
        .eq(BaseInfo::getOriginSys, "ASWAT")
        .lt(Objects.nonNull(lastId), BaseInfo::getId, lastId)
        .orderByDesc(BaseInfo::getId)
        .last(PageConstant.formatLimit(20000))
        .list();
  }

  @Override
  public Map<Long, UserBaseInfoDTO> mapUserBaseInfoDTO(Collection<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return Maps.newHashMap();
    }
    List<BaseInfo> baseInfos = listByUserId(userIds);
    List<UserBaseInfoDTO> userBaseInfoDTOS = userBaseInfoConvertor.toListUserBaseInfo(baseInfos);
    return Optional.ofNullable(userBaseInfoDTOS)
            .map(userBaseInfoList -> userBaseInfoList.stream()
                    .collect(Collectors.toMap(UserBaseInfoDTO::getId , this::setAccountStatus)))
            .orElse(Maps.newHashMap());
  }

  private UserBaseInfoDTO setAccountStatus(UserBaseInfoDTO userBaseInfoDTO) {
    if (userBaseInfoDTO.getFreezingTime().after(TimestampUtils.now())) {
      userBaseInfoDTO.setAccountStatus(AccountStatusEnum.FREEZE.name());
      userBaseInfoDTO.setAccountStatusName(AccountStatusEnum.FREEZE.name());
      return userBaseInfoDTO;
    }

    if (Objects.equals(userBaseInfoDTO.getAccountStatus(), AccountStatusEnum.FREEZE.name())) {
      userBaseInfoDTO.setAccountStatus(AccountStatusEnum.NORMAL.name());
      return userBaseInfoDTO;
    }

    return userBaseInfoDTO;
  }

  @Override
  public boolean updateAccountFreeze(Long userId, Integer days) {
    return update()
        .set(BaseInfo::getAccountStatus, AccountStatusEnum.FREEZE)
        .set(BaseInfo::getFreezingTime, TimestampUtils.nowPlusDays(days))
        .eq(BaseInfo::getId, userId)
        .execute();
  }

  @Override
  public boolean restoreDel(Long userId) {
    return update()
            .set(BaseInfo::getDel, Boolean.FALSE)
            .eq(BaseInfo::getId, userId)
            .execute();
  }
}
