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

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.IncomeExpenditureEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
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.dto.ResultResponse;
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.AppRankCountClient;
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.user.user.InviteUserClient;
import com.red.circle.other.inner.endpoint.user.user.UserOneTimeTaskClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
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.user.UserOneTimeTaskDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.user.user.UserSvipClient;
import com.red.circle.wallet.app.dto.cmd.FreightDealerToUserShipCmd;
import com.red.circle.wallet.domain.gateway.FreightShipGateway;
import com.red.circle.wallet.domain.gateway.WalletGoldGateway;
import com.red.circle.wallet.domain.wallet.WalletReceipt;
import com.red.circle.wallet.infra.database.cache.service.UserPayAuthCacheService;
import com.red.circle.wallet.infra.database.rds.entity.auth.PayAuth;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalance;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalanceRunningWater;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSeller;
import com.red.circle.wallet.infra.database.rds.service.auth.PayAuthService;
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.infra.database.rds.service.freight.FreightSellerService;
import com.red.circle.wallet.infra.sync.ControlSwitch;
import com.red.circle.wallet.inner.error.FreightErrorCode;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import com.red.circle.wallet.inner.model.enums.FreightBalanceOrigin;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
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 lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 经销商卖家给用户发货.
 *
 * @author zongpubin on 2023/5/23
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DealerToUserShipCmdExe {

  private final ControlSwitch controlSwitch;
  private final RedisService redisService;
  private final PayAuthService payAuthService;
  private final UserSvipClient userSvipClient;
  private final WalletGoldGateway walletGoldGateway;
  private final FreightShipGateway freightShipGateway;
  private final AppRankCountClient appRankCountClient;
  private final FreightSellerService freightSellerService;
  private final FreightBalanceService freightBalanceService;
  private final UserPayAuthCacheService userPayAuthCacheService;
  private final UserRechargeCountClient userRechargeCountClient;
  private final CumulativeRechargeClient cumulativeRechargeClient;
  private final FreightBalanceRunningWaterService freightBalanceRunningWaterService;
    private final PropsActivityClient propsActivityClient;
    private final UserOneTimeTaskClient userOneTimeTaskClient;
    private final UserProfileClient userProfileClient;
    private final InviteUserClient inviteUserClient;

  public BigDecimal execute(FreightDealerToUserShipCmd cmd) {
    controlSwitch.execute(cmd.requireReqSysOriginChildEnum());
    log.warn("{}", JacksonUtils.toJson(cmd));
//    ResponseAssert.isTrue(CommonErrorCode.REPEATED_SUBMISSION, redisService.setIfAbsent(
//            "DTS:" + cmd.getReqTraceId(),
//            1,
//            TimeUnit.MINUTES
//        )
//    );

    FreightSeller freightSeller = freightSellerService.getFreightSellerByUserId(
        cmd.requiredReqUserId());

    ResponseAssert.notNull(FreightErrorCode.NOT_SELLER, freightSeller);

    FreightBalance freightBalance = freightBalanceService.getFreightBalanceById(
        freightSeller.getFreightId());

    ResponseAssert.notNull(FreightErrorCode.NOT_FREIGHT, freightBalance);

    // 账号已关闭
    ResponseAssert.isFalse(UserErrorCode.ACCOUNT_CLOSED,
        freightBalanceService.checkClose(cmd.requireReqSysOriginChildEnum(),
            freightBalance.getUserId()));

    ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
        ArithmeticUtils.gte(cmd.getQuantity(), BigDecimal.ZERO));

    // 支付密码效验
    checkPayPassword(cmd);

    if (freightSeller.getEarnGold().compareTo(BigDecimal.ZERO) > 0) {
      // 授权余额不足
      ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE,
          ArithmeticUtils.gte(
              freightSeller.getEarnGold().subtract(freightSeller.getConsumptionGold()),
              cmd.getQuantity()));
    }

    BigDecimal balanceOld = freightBalanceService.getAvailableBalance(freightBalance.getUserId());
    // 经销商余额不足
    ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE,
        ArithmeticUtils.gte(balanceOld, cmd.getQuantity()));

    ResponseAssert.isTrue(CommonErrorCode.RESOURCE_RESTRICT_ACCESS,
        redisService.lock(getLockKey(cmd), 10));
    try {

      // 扣款失败
      ResponseAssert.isTrue(WalletErrorCode.DEDUCTION_FAILED,
          freightBalanceService
              .decrCandyBalance(cmd.requireReqSysOriginEnum(), freightBalance.getUserId(),
                  cmd.getQuantity()));

      BigDecimal balance = freightBalanceService.getAvailableBalance(freightBalance.getUserId());

      // 插入流水
      FreightBalanceRunningWater runningWater = saveRunningWater(cmd, balance,
          freightBalance.getUserId(), freightSeller.getId());

      // 发货
      walletGoldGateway.changeBalance(new WalletReceipt()
          .setReqTraceId(cmd.getReqTraceId())
          .setReceiptType(ReceiptType.INCOME)
          .setUserId(cmd.getAcceptUserId())
          .setSysOrigin(cmd.requireReqSysOrigin())
          .setEventType(Objects.toString(GoldOrigin.SELLER_AGENT))
          .setEventDesc(GoldOrigin.SELLER_AGENT.getDesc())
          .setEventId(Objects.toString(runningWater.getId()))
          .setAmount(PennyAmount.ofDollar(cmd.getQuantity().longValue()))
          .setOpUserType(OpUserType.APP)
          .setCreateTime(cmd.reqTimeElseNow())
      );

      if (freightSeller.getEarnGold().compareTo(BigDecimal.ZERO) > 0) {
        // 更新卖家已卖余额
        freightSellerService.updateSelectiveById(new FreightSeller()
            .setId(freightSeller.getId())
            .setConsumptionGold(freightSeller.getConsumptionGold().add(cmd.getQuantity()))
        );
      }

      // 累计充值
      incrRechargeQuantity(cmd);

      //累计今日充值，每日累计充值抽奖活动
      cumulativeRechargeClient.incrTodayRechargeScore(cmd.getAcceptUserId(),
          getGoldToSUD(cmd).doubleValue());

      // 余额如果等于0则回收货运代理徽章
      freightShipGateway.removeFreightShipBadge(freightBalance.getUserId(), balance);
      // SVip积分累计.
      userSvipClient.incrIntegral(cmd.getAcceptUserId(), cmd.getQuantity().longValue());

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

        completedFirstCharge(cmd.getAcceptUserId(), cmd.getReqSysOrigin().getOrigin());
      return balance;
    } finally {
      redisService.unlock(getLockKey(cmd));
    }
  }

  private void checkPayPassword(FreightDealerToUserShipCmd cmd) {
    List<Long> ignoreUserIds = List.of(
        1639905817928880129L, 1680509528015880193L
    );
    if (ignoreUserIds.contains(cmd.requiredReqUserId())) {
      return;
    }
    boolean enable = cmd.originEqAswat() && cmd.checkAndroid();
    if (!enable) {
      return;
    }
    if (!ArithmeticUtils.gte(cmd.getQuantity(), new BigDecimal("80000"))) {
      return;
    }

    // 查询密码输入错误次数
    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);
    }
  }

    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() && 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);
            }*/
        }
    }


    private void incrRechargeQuantity(FreightDealerToUserShipCmd cmd) {
    userRechargeCountClient.count(cmd.getAcceptUserId(),
        getGoldToSUD(cmd),
        MonthlyRechargeType.SELLER_AGENT
    );
    appRankCountClient.incrRechargeQuantity(cmd.requireReqSysOrigin(),
        cmd.getAcceptUserId(),
        cmd.getQuantity().longValue()
    );
  }

  private BigDecimal getGoldToSUD(FreightDealerToUserShipCmd cmd) {
    if (Objects.isNull(cmd.getQuantity())) {
      return BigDecimal.ZERO;
    }
    int ratio = freightShipGateway
        .getFreightGoldToUsdRatioCumulativeRecharge(cmd.getAcceptUserId());

    return cmd.getQuantity().divide(BigDecimal.valueOf(ratio), 2, RoundingMode.DOWN);
  }

  private FreightBalanceRunningWater saveRunningWater(FreightDealerToUserShipCmd cmd,
      BigDecimal balance, Long userId, Long sellerId) {
    FreightBalanceRunningWater freightBalanceRunningWater = new FreightBalanceRunningWater()
        .setSysOrigin(cmd.requireReqSysOrigin())
        .setUserId(userId)
        .setAcceptUserId(cmd.getAcceptUserId())
        .setSellerId(sellerId)
        .setType(IncomeExpenditureEnum.EXPENDITURE.getValue())
        .setQuantity(cmd.getQuantity())
        .setOrigin(FreightBalanceOrigin.SHIPMENT.name())
        .setBalance(balance)
        .setUsdQuantity(getGoldToSUD(cmd))
        .setRemark("经销商卖家出货");
    freightBalanceRunningWaterService.save(freightBalanceRunningWater);
    return freightBalanceRunningWater;
  }

  private String getLockKey(FreightDealerToUserShipCmd cmd) {
    return "FREIGHT_SHIP_SELLER_SEND" + cmd.getAcceptUserId() + "_"
        + cmd.getReqUserId();
  }


}
