package com.red.circle.other.app.command.activity;

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.order.inner.endpoint.UserRechargeCountClient;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.app.dto.clientobject.activity.LotteryWithdrawAmountCO;
import com.red.circle.other.app.dto.cmd.activity.LotteryExchangeGoldCmd;
import com.red.circle.other.app.service.activity.LotteryActivityRestService;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryUserProgress;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryWithdraw;
import com.red.circle.other.infra.database.rds.service.activity.LotteryUserProgressService;
import com.red.circle.other.infra.database.rds.service.activity.LotteryWithdrawService;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.other.inner.model.dto.user.SysExchangeGoldTipDTO;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 可提现金额兑换金币执行器.
 *
 * @author system
 * @since 2025-10-24
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LotteryExchangeGoldExe {

  private final RedisService redisService;
  private final LotteryWithdrawService lotteryWithdrawService;
  private final UserRegionClient userRegionClient;
  private final WalletGoldClient walletGoldClient;
  private final LotteryWithdrawAmountQryExe lotteryWithdrawAmountQryExe;
  private final UserRechargeCountClient userRechargeCountClient;

  public void execute(LotteryExchangeGoldCmd cmd) {
    ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
        ArithmeticUtils.gtZero(cmd.getAmount()));

    // 上锁
    String key = "LotteryExchangeGold:" + cmd.requiredReqUserId();
    ResponseAssert.isTrue(CommonErrorCode.REQUEST_LIMITING, redisService.lock(key, 60));

    try {
      Long userId = cmd.requiredReqUserId();

      // 1. 获取可提现金额
      LotteryWithdrawAmountCO withdrawAmount = lotteryWithdrawAmountQryExe.execute(userId, cmd.getActivityId());
      BigDecimal availableAmount = withdrawAmount.getAvailableAmount();

      // 2. 验证金额是否足够
      ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE, ArithmeticUtils.gte(availableAmount, cmd.getAmount()));

      // 3. 获取区域兑换金币比例
      Double goldRatio = getExchangeGoldRatio(cmd);

      // 4. 计算金币数量
      BigDecimal goldQuantity = BigDecimal.valueOf(goldRatio)
          .multiply(cmd.getAmount())
          .setScale(0, RoundingMode.DOWN);

      // 5. 记录兑换（作为提现记录）
      LotteryWithdraw withdraw = new LotteryWithdraw();
      withdraw.setUserId(userId);
      withdraw.setWithdrawNo(generateExchangeWithdrawNo());
      withdraw.setActivityId(cmd.getActivityId());
      withdraw.setAmount(cmd.getAmount());
      withdraw.setType("EXCHANGE");
      withdraw.setStatus(3); // 已完成
      withdraw.setDescription("兑换金币: " + goldQuantity + "金币");
      withdraw.setCreateTime(TimestampUtils.now());
      withdraw.setUpdateTime(TimestampUtils.now());
      lotteryWithdrawService.save(withdraw);

      // 6. 充值金币给用户自己
      GoldReceiptCmd build = GoldReceiptCmd.builder()
          .appIncome()
          .userId(userId)
          .amount(goldQuantity)
          .eventId(withdraw.getId().toString())
          .remark("抽奖活动兑换金币")
          .sysOrigin(cmd.getReqSysOrigin().getOrigin())
          .origin(GoldOrigin.LOTTERY_EXCHANGE_GOLD)
          .build();
      ResponseAssert.requiredSuccess(walletGoldClient.changeBalance(build));

      userRechargeCountClient.count(  cmd.getReqUserId(),
              cmd.getAmount(),
              MonthlyRechargeType.LOTTERY_EXCHANGE
      );

      log.info("用户{}兑换金币成功，金额{}，金币{}", userId, cmd.getAmount(), goldQuantity);
    } finally {
      redisService.unlock(key);
    }
  }

  /**
   * 生成提现单号.
   */
  private String generateExchangeWithdrawNo() {
    return "EX" + System.currentTimeMillis() +
        String.format("%06d", ThreadLocalRandom.current().nextInt(1000000));
  }


  /**
   * 获得1$兑换多少金币比例，基于区域+用户输入的美元.
   *
   * @return 1美元可获得的金币数量.
   */
  private Double getExchangeGoldRatio(LotteryExchangeGoldCmd cmd) {
    List<SysExchangeGoldTipDTO> exchangeGolds = ResponseAssert.requiredSuccess(
        userRegionClient.listRegionExchangeGoldTip(cmd.requiredReqUserId(), false));
    ResponseAssert.notEmpty(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds);

    exchangeGolds = exchangeGolds.stream().filter(gold ->
            cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getStart())) >= 0 &&
                cmd.getAmount().compareTo(BigDecimal.valueOf(gold.getEnd())) <= 0)
        .collect(Collectors.toList());
    ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, exchangeGolds);

    SysExchangeGoldTipDTO exchangeGold = exchangeGolds.get(0);
    ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, exchangeGold);
    ResponseAssert.isFalse(CommonErrorCode.CONFIGURATION_ERROR,
        Objects.isNull(exchangeGold.getValue()) || exchangeGold.getValue() <= 0);

    return exchangeGold.getValue();
  }

}