package com.red.circle.other.infra.gateway.user;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.RechargeCommissionResult;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.domain.ranking.RankingCycleType;
import com.red.circle.other.domain.ranking.RankingDimension;
import com.red.circle.other.infra.database.mongo.entity.activity.RankingActivityRecord;
import com.red.circle.other.infra.database.rds.dao.user.user.UserInviteUserDAO;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteUserRechargeCommission;
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
import com.red.circle.other.infra.database.rds.service.user.user.InviteUserRechargeCommissionService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteUserSummaryService;
import com.red.circle.other.infra.database.cache.service.user.UserInviteCacheService;
import com.red.circle.other.infra.gateway.RankingActivityGateway;
import com.red.circle.other.domain.gateway.user.ability.InviteUserGateway;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 被邀请的用户充值，邀请人获得佣金处理 实现.
 *
 * @author pengliang on 2023/12/17
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class InviteUserGatewayImpl implements InviteUserGateway {

  private static final String INVITE_RECHARGE_TOTAL_KEY = "invite:recharge:total:%d:%d";
  private static final long INVITE_RECHARGE_TOTAL_EXPIRE_DAYS = 30L;

  private final WalletGoldClient walletGoldClient;
  private final UserInviteCacheService userInviteCacheService;
  private final InviteUserSummaryService inviteUserSummaryService;
  private final UserInviteUserDAO userInviteUserDAO;
  private final InviteUserRechargeCommissionService inviteUserRechargeCommissionService;
  private final RankingActivityGateway rankingActivityGateway;
  private final RedisService redisService;

  @Override
  public RechargeCommissionResult getRechargeCommission(BigDecimal rechargeAmount, String sysOrigin,
      Long inviteUserId) {

    //根据被邀请人获得邀请人
    UserInviteUser userInviteUser = getByInviteUserId(inviteUserId);
    if (Objects.isNull(userInviteUser)) {
      return null;
    }

    //能够赚取佣金的天数
    Integer commissionDays = getCommissionDays(sysOrigin);
    if (Objects.isNull(commissionDays)) {
      log.info("能够赚取佣金的天数 => null, inviteUserId:{}", inviteUserId);
      return null;
    }

    Timestamp limitedTime = getLimitedTime(userInviteUser, commissionDays);
    if (limitedTime.compareTo(TimestampUtils.now()) < 0) {
      log.info("邀请时间 + 有效天数 < 当前时间, inviteUserId:{}", inviteUserId);
      return null;
    }

    Double commissionRate = getCommissionRate(sysOrigin);
    if (Objects.isNull(commissionRate)) {
      log.info("获得佣金比例 => null, inviteUserId:{}", inviteUserId);
      return null;
    }

    //获得的佣金 = 糖果数 * 佣金比例
    BigDecimal commission = getCommission(rechargeAmount, commissionRate);
    if (commission.compareTo(BigDecimal.ZERO) > 0) {
      inviteUserSummaryService.incrCommission(userInviteUser.getUserId(), commission);
      inviteUserRechargeCommissionService.add(sysOrigin, userInviteUser.getUserId(),
              userInviteUser.getInviteUserId(), commission);

      // 累计邀请人数：判断inviteUserRechargeCommissionService里是否已有该被邀请人的记录
      // 查询该被邀请人在此之前是否有充值记录
      long recordCount = inviteUserRechargeCommissionService.query()
              .eq(InviteUserRechargeCommission::getUserId, userInviteUser.getUserId())
              .eq(InviteUserRechargeCommission::getInviteUserId, userInviteUser.getInviteUserId())
              .count();

      // 如果记录数 < 1，说明是首次充值（当前这条刚插入），才累计邀请人数+1
      if (recordCount < 1) {
        log.info("被邀请人首次充值，累计邀请人数+1, inviterUserId:{}, inviteeUserId:{}",
                userInviteUser.getUserId(), userInviteUser.getInviteUserId());
        inviteUserSummaryService.incrUser(userInviteUser.getUserId(), 1);
      }

      awardCommission(sysOrigin, userInviteUser.getUserId(), commission, userInviteUser.getInviteUserId());

      recordRanking(userInviteUser.getUserId(), commission.longValue());
    }

    BigDecimal totalRecharge = accumulateRecharge(userInviteUser.getUserId(), inviteUserId, rechargeAmount);

    RechargeCommissionResult result = new RechargeCommissionResult();
    result.setInviterUserId(userInviteUser.getUserId());
    result.setInviteeUserId(inviteUserId);
    result.setInviteeTotalRecharge(totalRecharge);
    result.setSysOrigin(sysOrigin);
    result.setNewUser(isNewUserWithinDays(userInviteUser, 7));
    return result;
  }

  private BigDecimal accumulateRecharge(Long inviterUserId, Long inviteeUserId, BigDecimal rechargeAmount) {
    String key = String.format(INVITE_RECHARGE_TOTAL_KEY, inviterUserId, inviteeUserId);
    Long total = redisService.increment(key, rechargeAmount.longValue());
    if (total != null && total <= rechargeAmount.longValue()) {
      redisService.expire(key, INVITE_RECHARGE_TOTAL_EXPIRE_DAYS, TimeUnit.DAYS);
    }
    return total != null ? BigDecimal.valueOf(total) : rechargeAmount;
  }

  private void recordRanking(Long userId, Long quantity) {
    RankingActivityRecord record = new RankingActivityRecord();
    record.setSysOrigin(SysOriginPlatformEnum.LIKEI.name());
    record.setUserId(userId);
    record.setUserSex(1);
    record.setActivityType(RankingActivityType.INVITE);
    record.setCycleType(RankingCycleType.WEEKLY);
    record.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
    record.setDimension(RankingDimension.CONSUME_AMOUNT);
    record.setExpiredTime(new Timestamp(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
    rankingActivityGateway.accumulateRankingData(record, quantity);
  }

  /**
   * 奖励佣金
   *
   * @param sysOrigin    系统
   * @param userId       邀请人(获得佣金的人)
   * @param amount       佣金
   * @param inviteUserId 被邀请人(充值人)
   */
  private void awardCommission(String sysOrigin, Long userId, BigDecimal amount, Long inviteUserId) {
    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
            .appIncome()
            .userId(userId)
            .eventId(inviteUserId)
            .sysOrigin(sysOrigin)
            .origin(GoldOrigin.INVITE_USER_REWARDS)
            .originDescribe(GoldOrigin.INVITE_USER_REWARDS.getDesc())
            .amount(amount)
            .closeDelayAsset()
            .build());
  }

  private BigDecimal getCommission(BigDecimal rechargeAmount, Double commissionRate) {
    return rechargeAmount.multiply(BigDecimal.valueOf(commissionRate))
        .setScale(0, RoundingMode.DOWN);
  }

  private Double getCommissionRate(String sysOrigin) {
    return userInviteCacheService.getCommissionRate(SysOriginPlatformEnum.valueOf(sysOrigin));
  }

  /** 判断绑定时间是否为N天内 */
  private boolean isNewUserWithinDays(UserInviteUser userInviteUser, int days) {
    Duration duration = Duration.between(userInviteUser.getCreateTime().toLocalDateTime(), LocalDateTime.now());
    return duration.toDays() < days;
  }

  private UserInviteUser getByInviteUserId(Long inviteUserId) {
    if (Objects.isNull(inviteUserId)) {
      return null;
    }

    List<UserInviteUser> allInvites = userInviteUserDAO.selectList(
        new LambdaQueryWrapper<UserInviteUser>()
            .eq(UserInviteUser::getInviteUserId, inviteUserId)
            .orderByDesc(UserInviteUser::getCreateTime)
    );
    if (CollectionUtils.isEmpty(allInvites)) {
      return null;
    }

    return allInvites.get(0);
  }

  @Override
  public Long getUserByInviteUserId(Long inviteUserId) {
    if (Objects.isNull(inviteUserId)) {
      return null;
    }

    List<UserInviteUser> allInvites = userInviteUserDAO.selectList(
            new LambdaQueryWrapper<UserInviteUser>()
                    .eq(UserInviteUser::getInviteUserId, inviteUserId)
                    .orderByDesc(UserInviteUser::getCreateTime)
    );
    if (CollectionUtils.isEmpty(allInvites)) {
      return null;
    }

    return allInvites.get(0).getUserId();
  }

  private Integer getCommissionDays(String sysOrigin) {
    return userInviteCacheService.getCommissionDays(SysOriginPlatformEnum.valueOf(sysOrigin));
  }

  private Timestamp getLimitedTime(UserInviteUser userInviteUser, Integer commissionDays) {
    return TimestampUtils.dateTimestampPlusDays(userInviteUser.getCreateTime().toLocalDateTime(), commissionDays);
  }

}
