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

import com.red.circle.common.business.core.enums.OpUserType;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.component.mq.MessageEventProcess;
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.framework.dto.ResultResponse;
import com.red.circle.order.inner.endpoint.UserFreightRechargeRecordClient;
import com.red.circle.order.inner.endpoint.UserRechargeCountClient;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.inner.endpoint.activity.CumulativeRechargeClient;
import com.red.circle.other.inner.endpoint.activity.PropsActivityClient;
import com.red.circle.other.inner.endpoint.sys.EnumConfigClient;
import com.red.circle.other.inner.endpoint.team.target.UserBillDiamondBalanceClient;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.other.inner.endpoint.user.user.*;
import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.model.cmd.activity.SendActivityRewardCmd;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.other.inner.model.dto.agency.agency.BillDiamondBalanceDTO;
import com.red.circle.other.inner.model.dto.user.SysExchangeGoldTipDTO;
import com.red.circle.other.inner.model.dto.user.UserOneTimeTaskDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
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.convertor.SalaryAccountConvertor;
import com.red.circle.wallet.app.dto.clientobject.UserBankExchangeGoldResultCO;
import com.red.circle.wallet.app.dto.cmd.SalaryWithdrawCmd;
import com.red.circle.wallet.app.dto.cmd.UserBankExchangeGoldCmd;
import com.red.circle.wallet.app.dto.cmd.UserBillExchangeGoldCmd;
import com.red.circle.wallet.app.service.SalaryAccountService;
import com.red.circle.wallet.domain.gateway.SalaryAccountGateway;
import com.red.circle.wallet.domain.gateway.WalletGoldGateway;
import com.red.circle.wallet.domain.salary.SalaryReceipt;
import com.red.circle.wallet.domain.salary.SalaryReceiptRes;
import com.red.circle.wallet.domain.salary.SalaryType;
import com.red.circle.wallet.domain.wallet.WalletReceipt;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankRunningWater;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankWithdrawGoldApply;
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.mongo.service.bank.UserBankWithdrawGoldApplyService;
import com.red.circle.wallet.infra.database.rds.entity.diamond.SalaryDiamondBalance;
import com.red.circle.wallet.infra.database.rds.entity.diamond.SalaryDiamondRunningWater;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalanceRunningWater;
import com.red.circle.wallet.infra.database.rds.service.diamond.SalaryDiamondBalanceService;
import com.red.circle.wallet.infra.database.rds.service.diamond.SalaryDiamondRunningWaterService;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightBalanceRunningWaterService;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightBalanceService;
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.FreightBalanceOrigin;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import com.red.circle.wallet.inner.model.enums.GoldWithdrawAcceptAccount;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 用户工资余额兑换金币.
 */
@Component
@RequiredArgsConstructor
public class SalaryExchangeGoldCmdExe {

  private static final Logger log = LoggerFactory.getLogger(SalaryExchangeGoldCmdExe.class);
  private final RedisService redisService;
  private final UserRegionClient userRegionClient;
  private final WalletGoldGateway walletGoldGateway;
  private final FreightBalanceService freightBalanceService;
  private final UserFreightRechargeRecordClient userFreightRechargeRecordClient;
  private final UserBankWithdrawGoldApplyService userBankWithdrawGoldApplyService;
  private final FreightBalanceRunningWaterService freightBalanceRunningWaterService;
  private final UserRechargeCountClient userRechargeCountClient;
    private final SalaryAccountGateway salaryAccountGateway;
    private final SalaryAccountConvertor salaryAccountConvertor;
    private final PropsActivityClient propsActivityClient;
    private final UserOneTimeTaskClient userOneTimeTaskClient;
    private final UserProfileClient userProfileClient;
    private final InviteUserClient inviteUserClient;

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

    String key = "USEGold:" + cmd.getReqUserId();
    ResponseAssert.isTrue(CommonErrorCode.REQUEST_LIMITING, redisService.lock(key, 60));

    BigDecimal balance = salaryAccountGateway.getAvailableBalance(cmd.getReqUserId(), SalaryType.BD_SALARY).getDollarAmount();
    ResponseAssert.isFalse(WalletErrorCode.INSUFFICIENT_BALANCE,
        !ArithmeticUtils.gte(balance, cmd.getAmount()));

    boolean freight = freightBalanceService.existsBalance(cmd.getReqUserId());

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

    // 扣钱
    SalaryReceipt receipt = salaryAccountConvertor.toExchangeReceipt(cmd);
    SalaryReceiptRes salaryReceiptRes = salaryAccountGateway.changeBalance(receipt);
    ResponseAssert.notNull(WalletErrorCode.INSUFFICIENT_BALANCE, salaryReceiptRes);

    BigDecimal goldQuantity = BigDecimal.valueOf(goldRatio).multiply(cmd.getAmount()).setScale(0, RoundingMode.DOWN);
    Long bankRunWaterId = IdWorkerUtils.getId();
    Long applyId = IdWorkerUtils.getId();

    // 申请详情
    userBankWithdrawGoldApplyService.add(
        new UserBankWithdrawGoldApply()
            .setId(applyId)
            .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
            .setAcceptAccount((freight
                ? GoldWithdrawAcceptAccount.FREIGHT
                : GoldWithdrawAcceptAccount.GOLD).name())
            .setAcceptUserId(cmd.getReqUserId())
            .setSubmitUserId(cmd.getReqUserId())
            .setAmount(cmd.getAmount())
            .setGoldQuantity(goldQuantity.stripTrailingZeros().toPlainString())
            .setExchangeRate(Objects.toString(goldRatio))
            .setCreateTime(TimestampUtils.now())
            .setUpdateTime(TimestampUtils.now())
            .setCreateUser(cmd.getReqUserId())
            .setUpdateUser(cmd.getReqUserId())
            .setCreateUserOrigin(OpUserType.APP.getType())
            .setUpdateUserOrigin(OpUserType.APP.getType())
    );

    sendGold(cmd, freight, goldQuantity, bankRunWaterId, "银行卡兑换");

    // 记录充值金额
    rechargeRecord(cmd);

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

      // 被邀请人充值邀请人获得佣金
      inviteUserClient.processRechargeCommission(goldQuantity,
              cmd.getReqSysOrigin().getOrigin(), cmd.getReqUserId());

      completedFirstCharge(cmd.getReqUserId(), cmd.getReqSysOrigin().getOrigin());
    redisService.unlock(key);
    return new UserBankExchangeGoldResultCO()
        .setBankBalance(salaryReceiptRes.getAvailableBalance().getDollarAmount())
        .setExchangeGold(Objects.toString(goldRatio));
  }

  private void rechargeRecord(SalaryWithdrawCmd cmd) {
    userFreightRechargeRecordClient.inrNowMonthAmount(
        cmd.getReqUserId(),
        cmd.getAmount()
    );
  }

    private void completedFirstCharge(Long userId, String sysOrigin) {
        UserProfileDTO userProfileDTO = userProfileClient.getByUserId(userId).getBody();
        Duration duration = Duration.between(userProfileDTO.getCreateTime().toLocalDateTime(), LocalDateTime.now());
        if (duration.toDays() >= 7) {
            return;
        }

        ResultResponse<UserOneTimeTaskDTO> response = userOneTimeTaskClient.queryCompletedFirstCharge(userId, sysOrigin);
        if (response.checkSuccess()) {
            if (response.getBody() == null) {
                userOneTimeTaskClient.completedFirstCharge(userId, sysOrigin);

                // 发送首充奖励
                List<ActivityPropsRule> body = propsActivityClient.listRule(SysOriginPlatformEnum.valueOf(sysOrigin),
                        PropsActivityTypeEnum.FIRST_CHARGE_REWARD).getBody();
                if (body != null) {
                    ActivityPropsRule ruleConfig = body.get(0);

                    propsActivityClient.sendActivityReward(
                            new SendActivityRewardCmd()
                                    .setTrackId(ruleConfig.getId())
                                    .setOrigin(SendPropsOrigin.FIRST_CHARGE_REWARD)
                                    .setSysOrigin(SysOriginPlatformEnum.valueOf(ruleConfig.getSysOrigin()))
                                    .setSourceGroupId(ruleConfig.getResourceGroupId())
                                    .setAcceptUserId(userId)
                    );
                    userProfileClient.removeCacheAll(userId);
                }
            }
        }
    }

  /**
   * 获得1$兑换多少金币比例， 基于区域+用户输入的美元.
   *
   * @return 1美元可获得的金币数量.
   */
  private Double getExchangeGoldRatio(SalaryWithdrawCmd cmd) {

    boolean freight = freightBalanceService.existsBalance(cmd.getReqUserId());

    List<SysExchangeGoldTipDTO> exchangeGolds = ResponseAssert.requiredSuccess(
        userRegionClient
            .listRegionExchangeGoldTip(cmd.getReqUserId(), freight)
    );
    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.notEmpty(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();
  }

  private void sendGold(SalaryWithdrawCmd cmd, boolean freight, BigDecimal goldQuantity,
      Long bankRunWaterId, String remark) {
    if (freight) {
      // 货运代理入账
      ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
          freightBalanceService
              .incrCandyBalance(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()),
                  cmd.getReqUserId(), goldQuantity));

      // 插入流水
      BigDecimal freightBalance = freightBalanceService.getAvailableBalance(
          cmd.getReqUserId());
      freightBalanceRunningWaterService.save(new FreightBalanceRunningWater()
          .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
          .setUserId(cmd.getReqUserId())
          .setAcceptUserId(cmd.getReqUserId())
          .setType(ReceiptType.INCOME.getType())
          .setQuantity(goldQuantity)
          .setUsdQuantity(cmd.getAmount())
          .setBalance(freightBalance)
          .setOrigin(FreightBalanceOrigin.PURCHASE.name())
          .setRemark(remark)
          .withCreateUser(cmd.getReqUserId()));
      return;
    }

    // 金币入账
    walletGoldGateway.changeBalance(new WalletReceipt()
        .setReqTraceId(cmd.getReqTraceId())
        .setReceiptType(ReceiptType.INCOME)
        .setUserId(cmd.getReqUserId())
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setEventType(Objects.toString(GoldOrigin.BANK_GOLD_COIN_EXCHANGE))
        .setEventDesc(GoldOrigin.BANK_GOLD_COIN_EXCHANGE.getDesc())
        .setEventId(Objects.toString(bankRunWaterId))
        .setAmount(PennyAmount.ofDollar(goldQuantity.longValue()))
        .setOpUserType(OpUserType.APP)
        .setCreateTime(cmd.getReqTime())
    );
  }

}
