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

import com.red.circle.common.business.enums.IncomeExpenditureEnum;
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.other.inner.endpoint.sys.EnumConfigClient;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.wallet.app.dto.cmd.FreightDealerShipCmd;
import com.red.circle.wallet.domain.gateway.FreightShipGateway;
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.FreightBalanceRunningWater;
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.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 java.math.BigDecimal;
import java.math.RoundingMode;
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 DealerToFreightShipCmdExe {

  private final RedisService redisService;
  private final ControlSwitch controlSwitch;
  private final PayAuthService payAuthService;
  private final UserRegionClient userRegionClient;
  private final EnumConfigClient enumConfigClient;
  private final UserProfileClient userProfileClient;
  private final FreightShipGateway freightShipGateway;
  private final FreightBalanceService freightBalanceService;
  private final UserPayAuthCacheService userPayAuthCacheService;
  private final FreightBalanceRunningWaterService freightBalanceRunningWaterService;

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

    // 账号已关闭
    ResponseAssert.isFalse(UserErrorCode.ACCOUNT_CLOSED,
        freightBalanceService.checkClose(cmd.requireReqSysOriginChildEnum(),
            cmd.requiredReqUserId()));
    // 是否为经销商
    ResponseAssert.isTrue(FreightErrorCode.NOT_DEALER,
        freightBalanceService.checkDealer(cmd.requireReqSysOriginChildEnum(),
            cmd.requiredReqUserId()));

    // 接收用户是否为是否为经销商
    ResponseAssert.isFalse(FreightErrorCode.IS_DEALER,
        freightBalanceService.checkDealer(cmd.requireReqSysOriginChildEnum(),
            cmd.getAcceptUserId()));

    // 接收用户是否为货运代理
    ResponseAssert.isTrue(FreightErrorCode.NOT_FREIGHT,
        freightBalanceService.existsBalance(cmd.getAcceptUserId()));

    // 发送人与接受人必须要在相同区域下才能交易
    ResponseAssert.isTrue(CommonErrorCode.REGION_NOT_SUPPORTED_ERROR,
        ResponseAssert.requiredSuccess(
            userRegionClient.checkEqRegion(cmd.requiredReqUserId(), cmd.getAcceptUserId()))
    );

    BigDecimal dealerRecharge = ResponseAssert.requiredSuccess(
        enumConfigClient.getValueBigDecimal(EnumConfigKey.ASWAT_DEALER_RECHARGE,
            cmd.requireReqSysOrigin())
    );

    // 配置错误
    ResponseAssert.isTrue(CommonErrorCode.CONFIGURATION_ERROR,
        ArithmeticUtils.gt(dealerRecharge, BigDecimal.ZERO));
    // 发送金币最低金额
    ResponseAssert.isTrue(FreightErrorCode.NOT_BELOW_SOME,
        ArithmeticUtils.gte(cmd.getQuantity(), dealerRecharge));

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

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

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

      BigDecimal balance = freightBalanceService.getAvailableBalance(cmd.requiredReqUserId());

      // 插入流水
      saveRunningWater(cmd, balance);

      // 发货
      freightBalanceService.incrCandyBalance(cmd.requireReqSysOriginEnum(), cmd.getAcceptUserId(),
          cmd.getQuantity());

      // 余额如果等于0则回收货运代理徽章
      freightShipGateway.removeFreightShipBadge(cmd.requiredReqUserId(), balance);
      return balance;
    } finally {
      redisService.unlock(getLockKey(cmd));
    }
  }

  private void checkPayPassword(FreightDealerShipCmd cmd) {
    if (Objects.equals(cmd.requiredReqUserId(), 1639905817928880129L) || Objects.equals(
        cmd.requiredReqUserId(), 1680509528015880193L)) {
      return;
    }
    if (cmd.originEqAswat() && cmd.checkAndroid()) {
      if (ArithmeticUtils.gte(cmd.getQuantity(), new BigDecimal("80000"))) {
        // 查询密码输入错误次数
        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 BigDecimal getGoldToSUD(FreightDealerShipCmd 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 void saveRunningWater(FreightDealerShipCmd cmd, BigDecimal balance) {
    FreightBalanceRunningWater freightBalanceRunningWater = new FreightBalanceRunningWater()
        .setId(IdWorkerUtils.getId())
        .setSysOrigin(cmd.requireReqSysOrigin())
        .setUserId(cmd.requiredReqUserId())
        .setAcceptUserId(cmd.getAcceptUserId())
        .setType(IncomeExpenditureEnum.EXPENDITURE.getValue())
        .setQuantity(cmd.getQuantity())
        .setOrigin(FreightBalanceOrigin.SHIPMENT.name())
        .setBalance(balance)
        .setUsdQuantity(getGoldToSUD(cmd))
        .setRemark("经销商出货");
    // 出货流水
    freightBalanceRunningWaterService.save(freightBalanceRunningWater);
    // 进货流水
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(cmd.requiredReqUserId()));
    if (Objects.nonNull(userProfile)) {
      freightBalanceRunningWater.setRemark("从[" + userProfile.getAccount() + "]经销商进货");
    }
    BigDecimal acceptBalance = freightBalanceService.getAvailableBalance(cmd.getAcceptUserId());
    freightBalanceRunningWater.setBalance(acceptBalance.add(cmd.getQuantity()));
    freightBalanceRunningWater.setId(IdWorkerUtils.getId());
    freightBalanceRunningWater.setUserId(cmd.getAcceptUserId());
    freightBalanceRunningWater.setType(IncomeExpenditureEnum.INCOME.getValue());
    freightBalanceRunningWater.setOrigin(FreightBalanceOrigin.PURCHASE.name());
    freightBalanceRunningWaterService.save(freightBalanceRunningWater);
  }

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