package com.red.circle.wallet.app.command.bank;

import com.alibaba.fastjson.JSON;
import com.red.circle.common.business.core.enums.OpUserType;
import com.red.circle.common.business.core.enums.ReceiptType;
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.other.inner.endpoint.team.target.TeamMemberClient;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.enums.team.TeamMemberRole;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.app.dto.clientobject.UserBankWithdrawResultCO;
import com.red.circle.wallet.app.dto.cmd.UserBankWithdrawTransferCmd;
import com.red.circle.wallet.infra.database.cache.service.UserPayAuthCacheService;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankRunningWater;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankBalanceService;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankRunningWaterService;
import com.red.circle.wallet.infra.database.rds.entity.auth.PayAuth;
import com.red.circle.wallet.infra.database.rds.service.auth.PayAuthService;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.math.BigDecimal;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

/**
 * 用户银行卡提现 转账.
 *
 * @author pengliang on 2023/2/20
 */
@Component
@Log4j2
@RequiredArgsConstructor
public class UserBankWithdrawTransferCmdExe {

  private final RedisService redisService;
  private final PayAuthService payAuthService;
  private final TeamMemberClient teamMemberClient;
  private final UserBankBalanceService userBankBalanceService;
  private final UserPayAuthCacheService userPayAuthCacheService;
  private final UserBankRunningWaterService userBankRunningWaterService;

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

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

    BigDecimal balance = userBankBalanceService.getBalance(cmd.requiredReqUserId())
        .getAccurateBalance();

    ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE,
        ArithmeticUtils.gte(balance, cmd.getAmount()));

//    UserBankCard bankCard = userBankCardService.getUseByUserId(cmd.getAcceptUserId());
//    ApiAssert.notNull(UserErrorEnum.BANK_CARD_NOT_FOUND, bankCard);

    // 扣钱
    BankBalanceDTO decrUserBankBalanceDTO = userBankBalanceService.decr(cmd.requiredReqUserId(),
        PennyAmount.ofDollar(cmd.getAmount()));
    ResponseAssert.notNull(WalletErrorCode.INSUFFICIENT_BALANCE, decrUserBankBalanceDTO);

    Long bankRunWaterId = IdWorkerUtils.getId();

    // 银行卡流水
    userBankRunningWaterService.add(
        new UserBankRunningWater()
            .setId(bankRunWaterId)
            .setUserId(cmd.requiredReqUserId())
            .setSysOrigin(cmd.requireReqSysOrigin())
            .setType(ReceiptType.EXPENDITURE.getType())
            .setAmount(cmd.getAmount())
            .setEvent(UserBankWaterEvent.TRANSFER.name())
            .setEventDescribe(UserBankWaterEvent.TRANSFER.getDescribe())
            .setRemark("Withdrawal transfer")
            .setTrackId(Objects.toString(cmd.getAcceptUserId()))
            .setBalance(decrUserBankBalanceDTO.getAccurateBalance())
            .setCreateTime(TimestampUtils.now())
            .setUpdateTime(TimestampUtils.now())
            .setCreateUser(cmd.getReqUserId())
            .setCreateUserOrigin(OpUserType.APP.getType())
    );

    // 转账
    userBankBalanceService.incr(cmd.getAcceptUserId(), cmd.requireReqSysOrigin(),
        PennyAmount.ofDollar(cmd.getAmount()));

    // 银行卡流水
    userBankRunningWaterService.add(
        new UserBankRunningWater()
            .setId(IdWorkerUtils.getId())
            .setUserId(cmd.getAcceptUserId())
            .setSysOrigin(cmd.requireReqSysOrigin())
            .setType(ReceiptType.INCOME.getType())
            .setAmount(cmd.getAmount())
            .setEvent(UserBankWaterEvent.RECEIVE_TRANSFER.name())
            .setEventDescribe(UserBankWaterEvent.RECEIVE_TRANSFER.getDescribe())
            .setRemark("Receive transfer")
            .setTrackId(Objects.toString(cmd.requiredReqUserId()))
            .setBalance(userBankBalanceService.getBalance(cmd.getAcceptUserId())
                .getAccurateBalance())
            .setCreateTime(TimestampUtils.now())
            .setUpdateTime(TimestampUtils.now())
            .setCreateUser(cmd.requiredReqUserId())
            .setCreateUserOrigin(OpUserType.APP.getType())
    );

    redisService.unlock(key);
    return new UserBankWithdrawResultCO()
        .setBankBalance(decrUserBankBalanceDTO.getAccurateBalance())
        .setWithdrawAmount(Objects.toString(cmd.getAmount()));
  }

  private void checkPayPassword(UserBankWithdrawTransferCmd cmd) {
    TeamMemberDTO teamMember = teamMemberClient.getByMemberId(cmd.requiredReqUserId())
        .getBody();
    log.warn("用户团队信息:{}"+ JSON.toJSONString(teamMember));
    if (Objects.nonNull(teamMember)
        && TeamMemberRole.OWN.eq(teamMember.getRole())) {
      return;
    }
    if (cmd.originEqAswat() && cmd.checkAndroid()) {
      // 查询密码输入错误次数
      Integer times = userPayAuthCacheService.getPayAuthTimes(cmd.requiredReqUserId());
      ResponseAssert.isFalse(WalletErrorCode.PAY_PASSWORD_EXHAUSTED, times > 3);
      PayAuth payAuth = payAuthService.getByPassword(cmd.getPassword(),
          cmd.requiredReqUserId());

      if (Objects.isNull(payAuth)) {
        // 密码输错次数
        userPayAuthCacheService.recordPayAuth(cmd.requiredReqUserId());
        ResponseAssert.isFalse(WalletErrorCode.PAY_PASSWORD_ERROR, Boolean.TRUE);
      }
    }
  }
}
