package com.red.circle.order.app.command.pay.app.execute;

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.PlatformChannelEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
import com.red.circle.mq.rocket.business.producer.OrderMqMessage;
import com.red.circle.order.app.command.pay.app.model.AppCommodity;
import com.red.circle.order.app.command.pay.app.model.ExtractInAppPurchaseReceipt;
import com.red.circle.order.app.command.pay.app.model.PurchaseProduct;
import com.red.circle.order.app.command.pay.app.strategy.commodity.FactoryForCommodityStrategy;
import com.red.circle.order.app.command.pay.app.strategy.extract.FactoryForExtractAppPurchaseOrderStrategy;
import com.red.circle.order.app.convertor.PurchaseAppConvertor;
import com.red.circle.order.app.dto.clientobject.PurchaseReceiptCO;
import com.red.circle.order.app.dto.cmd.AbstractPurchaseCmd;
import com.red.circle.order.domain.gateway.InAppPurchaseGateway;
import com.red.circle.order.domain.gateway.InAppPurchaseProductGateway;
import com.red.circle.order.domain.gateway.UserRechargeCountGateway;
import com.red.circle.order.domain.order.PurchaseHistory;
import com.red.circle.order.domain.product.IAppPurchaseProduct;
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseDetailsService;
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseEventNotice;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseFactory;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseProduct;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseStatusStep;
import com.red.circle.order.infra.database.rds.entity.order.OrderPurchaseFriendshipRecord;
import com.red.circle.order.infra.database.rds.service.order.OrderPurchaseFriendshipRecordService;
import com.red.circle.order.inner.asserts.OrderErrorCode;
import com.red.circle.order.inner.asserts.ProductErrorCode;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseReceiptType;
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseStatus;
import com.red.circle.other.inner.endpoint.activity.AppRankCountClient;
import com.red.circle.other.inner.endpoint.user.user.UserLevelClient;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.regex.RegexConstant;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringPool;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.other.inner.endpoint.user.user.UserSvipClient;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * app内购支付购买.
 *
 * @author pengliang on 2022/3/11
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AppInAppPurchaseCmdExe {

  private final OrderMqMessage orderMqMessage;
  private final UserSvipClient userSvipClient;
  private final UserRegionClient userRegionClient;
  private final AppRankCountClient appRankCountClient;
  private final PurchaseAppConvertor purchaseAppConvertor;
  private final InAppPurchaseGateway inAppPurchaseGateway;
  private final InAppPurchaseDetailsService inAppPurchaseDetailsService;
  private final UserRechargeCountGateway userRechargeCountGateway;
  private final FactoryForCommodityStrategy factoryForCommodityStrategy;
  private final InAppPurchaseProductGateway appPurchaseProductRepository;
  private final OrderPurchaseFriendshipRecordService orderPurchaseFriendshipRecordService;
  private final FactoryForExtractAppPurchaseOrderStrategy factoryForExtractAppPurchaseOrderStrategy;
  private final UserLevelClient userLevelClient;

  public PurchaseReceiptCO execute(AbstractPurchaseCmd cmd) {
    // 提取单据
    ExtractInAppPurchaseReceipt receipt = extractReceipt(cmd);
    // 获取购买商品
    PurchaseProduct purchaseProduct = getPurchaseProduct(cmd, receipt);
    purchaseProduct.setGroupName("CANDY");
    // 保存订单
    PurchaseHistory history = convertPurchaseHistory(cmd, receipt, purchaseProduct);
    saveOrderPurchaseHistory(history);

    AppCommodity appCommodity = new AppCommodity()
        .setFiendId(cmd.getFriendId())
        .setPurchaseHistory(history)
        .setPurchaseProduct(purchaseProduct);

    // 保存订单详情
    saveInAppPurchaseDetails(cmd, appCommodity);

    // 发送商品
    PurchaseReceiptCO purchaseReceipt = factoryForCommodityStrategy.getStrategy(
            appCommodity.getProductConfigGroupName())
        .doOperation(appCommodity);

    // 保存帮朋友购买记录
    if (Objects.nonNull(appCommodity.getFiendId()) && appCommodity.getFiendId() > 0) {
      saveFriendshipRecord(appCommodity);
    }

    BuySuccessEvent event = new BuySuccessEvent()
        .setPurchaseHistoryId(history.getId())
        .setFriendId(cmd.getFriendId())
        .setUserId(history.getUserId())
        .setSysOrigin(history.getSysOrigin())
        .setPlatform(history.getPlatform())
        .setPayPlatform(history.getPayPlatform())
        .setOrigin(appCommodity.getProductConfigProductId())
        .setOriginName(appCommodity.getOriginDescription())
        .setExpireDateMs(history.getExpiresDateMs())
        .setTrialPeriod(history.getTrialPeriod())
        .setBuyDateTime(TimestampUtils.convert(cmd.getReqTime()))
        .setProductConfig(
            purchaseAppConvertor.toPurchaseProductEvent(appCommodity.getPurchaseProduct()))
        .setCandyQuantity(purchaseReceipt.getObtainAmount())
        .setCandyBalance(purchaseReceipt.getBalanceAmount());
    incrRechargeQuantity(event);
    orderMqMessage.inAppPurchaseSuccess(event);
    return purchaseReceipt;
  }

  private void saveOrderPurchaseHistory(PurchaseHistory history) {

    // 订单已创建
    ResponseAssert.isFalse(OrderErrorCode.ORDER_EXISTS_CREATED,
        inAppPurchaseGateway.existOrder(history.getOrderId()));

    // 保存历史订单
    inAppPurchaseGateway.addInAppPurchase(history);
  }

  private void incrRechargeQuantity(BuySuccessEvent event) {
    // 累计充值
    userRechargeCountGateway.count(
        event.getAcceptUserId(),
        event.getProductConfig().getUnitPrice(),
        getMonthlyRechargeType(event));

    //计入财富榜
    userLevelClient.incrWealthExp(event.getAcceptUserId(), BigDecimal.valueOf(event.getProductConfig().getProductConfigCandy().longValue()));

    // SVip积分累计
    userSvipClient.incrIntegral(event.getAcceptUserId(), event.getCandyQuantity().longValue());

    // 充值榜
    appRankCountClient.incrRechargeQuantity(event.getSysOrigin(),
        event.getAcceptUserId(),
        event.getProductConfig().getProductConfigCandy().longValue());
  }

  private MonthlyRechargeType getMonthlyRechargeType(BuySuccessEvent event) {
    if (Objects.equals(event.getPayPlatform(), "GOOGLE")) {
      return MonthlyRechargeType.GOOGLE;
    }

    if (Objects.equals(event.getPayPlatform(), "APPLE")) {
      return MonthlyRechargeType.APPLE;
    }

    if (Objects.equals(event.getPayPlatform(), "HUAWEI")) {
      return MonthlyRechargeType.HUAWEI;
    }

    return MonthlyRechargeType.UNDEFINED;
  }

  private void saveFriendshipRecord(AppCommodity appCommodity) {
    PurchaseHistory purchaseHistory = appCommodity.getPurchaseHistory();
    orderPurchaseFriendshipRecordService.save(
        new OrderPurchaseFriendshipRecord()
            .setId(IdWorkerUtils.getId())
            .setUserId(purchaseHistory.getUserId())
            .setFriendId(appCommodity.getFiendId())
            .setOrderId(Objects.toString(purchaseHistory.getId()))
            .setProductId(Objects.isNull(appCommodity.getProductConfigId()) ? "0"
                : appCommodity.getProductConfigId().toString())
            .setDescription(appCommodity.getOriginDescription())
    );
  }

  private void saveInAppPurchaseDetails(AbstractPurchaseCmd cmd, AppCommodity commodity) {
    PurchaseHistory orderHistory = commodity.getPurchaseHistory();
    PurchaseProduct purchaseProduct = commodity.getPurchaseProduct();
    // 创建订单详情
    inAppPurchaseDetailsService.create(new InAppPurchaseDetails()
        .setId(Objects.toString(orderHistory.getId()))
        .setTrackId(orderHistory.getProductId())
        .setTrackCommodityType("GOLD")
        .setReceiptType(InAppPurchaseReceiptType.PAYMENT)
        .setSysOrigin(orderHistory.getSysOrigin())
        .setAcceptUserId(commodity.getAcceptUserId())
        .setOrderId(orderHistory.getOrderId())
        .setEnv(orderHistory.getEvn())
        .setFactory(
            new InAppPurchaseFactory()
                .setPlatform(orderHistory.getPlatform())
                .setFactoryCode(orderHistory.getPayPlatform())
                .setFactoryChannelCode(orderHistory.getPayPlatform())
        )
        .setProducts(
            List.of(new InAppPurchaseProduct()
                .setId(purchaseProduct.getId())
                .setName(purchaseProduct.getGroupName())
                .setCode(orderHistory.getPurchaseProductId())
                .setDescribe(purchaseProduct.getDescription())
                .setContent(commodity.getProductConfigCandy().stripTrailingZeros().toPlainString())
                .setAmountUsd(purchaseProduct.getUnitPrice())
                .setQuantity(1)
            )
        )
        .setCountryCode("")
        .setCurrency("USD")
        .setAmount(purchaseProduct.getUnitPrice())
        .setAmountUsd(purchaseProduct.getUnitPrice())
        .setTrialPeriod(Boolean.FALSE)
        .setStatus(InAppPurchaseStatus.SUCCESS)
        .setReason("")
        .setPurchaseDateMs(TimestampUtils.now())
        .setCreateTime(TimestampUtils.now())
        .setUpdateTime(TimestampUtils.now())
        .setCreateUser(orderHistory.getUserId())
        .setStatusSteps(List.of(
            new InAppPurchaseStatusStep()
                .setStatus(InAppPurchaseStatus.CREATE)
                .setCreateTime(TimestampUtils.now()),
            new InAppPurchaseStatusStep()
                .setStatus(InAppPurchaseStatus.SUCCESS)
                .setCreateTime(TimestampUtils.now())
        ))
        .setPayNotices(List.of(
            new InAppPurchaseEventNotice()
                .setEventId("custom-request-response-param")
                .setNoticeType("API-INTERNAL-PARAMETERS")
                .setNoticeData(cmd)
                .setCreateTime(TimestampUtils.now())
        ))
        .setMetadata(CollectionUtils.newHashMap())
        .setVersion(0)
    );
  }

  private PurchaseHistory convertPurchaseHistory(
      AbstractPurchaseCmd cmd,
      ExtractInAppPurchaseReceipt receipt,
      PurchaseProduct purchaseProduct) {

    PurchaseHistory dto = purchaseAppConvertor.toPurchaseHistory(receipt);
    dto.setId(IdWorkerUtils.getId());
    dto.setPurchaseProductId(Objects.toString(purchaseProduct.getId()));
    dto.setProductDescription(purchaseProduct.getDescription());
    dto.setProductType(purchaseProduct.getProductType());
    dto.setUnit("USD");
    dto.setUnitPrice(purchaseProduct.getUnitPrice());
    dto.setTrialPeriod(receipt.getTrialPeriod());
    dto.setExpandType(cmd.getExpandType());
    return dto;
  }

  private PurchaseProduct getPurchaseProduct(AbstractPurchaseCmd cmd,
      ExtractInAppPurchaseReceipt receipt) {

    IAppPurchaseProduct purchaseProduct = getPurchaseProductV2(cmd, receipt);

    ResponseAssert.notNull(ProductErrorCode.NOT_FOUND_PRODUCT, purchaseProduct);

    // 如2023-4-1号之后强更过，则可以删除这句判断
    if (Objects.nonNull(purchaseProduct) && Objects.nonNull(cmd.getFriendId())) {

      purchaseProduct = processFriendRegionProduct(cmd, purchaseProduct);
    }

    return purchaseAppConvertor.entityToPurchaseProduct(purchaseProduct);
  }

  /**
   * 如2023-4-1号之后强更过，则可以删除这句判断
   */
  private IAppPurchaseProduct processFriendRegionProduct(AbstractPurchaseCmd cmd,
      IAppPurchaseProduct purchaseProduct) {

    String buyUserRegionId = getUserRegionId(cmd.requiredReqUserId());
    String friendRegionId = getUserRegionId(cmd.getFriendId());
    // 同一区域则正常返回 purchaseProduct
    if (Objects.equals(buyUserRegionId, friendRegionId)) {
      return purchaseProduct;
    }

    // 不是同一区域则使用朋友所在区域的金币内购产品
    return Optional.ofNullable(appPurchaseProductRepository
            .getProduct(cmd.requireReqSysOriginChildEnum(),
                purchaseProduct.getPlatform(),
                purchaseProduct.getProductPackage(), friendRegionId))
        .orElse(purchaseProduct);

  }

  /**
   * 获得用户区域id.
   */
  private String getUserRegionId(Long userId) {
    return userRegionClient.getRegionId(userId).getBody();
  }

  private IAppPurchaseProduct getPurchaseProductV2(AbstractPurchaseCmd cmd,
      ExtractInAppPurchaseReceipt receipt) {

    String purchaseProduct = cmd.getProduct();
    // 产品不存在
    ResponseAssert.isTrue(ProductErrorCode.NOT_FOUND_PRODUCT,
        StringUtils.isNotBlank(purchaseProduct));

    ResponseAssert.notBlank(ProductErrorCode.PAY_TYPE_ERROR, cmd.getReqAppIntel().getChannel());

    PlatformChannelEnum channelEnum = cmd.checkIos() ? PlatformChannelEnum.AppStore : PlatformChannelEnum.GOOGLE;

    String regionId = userRegionClient.getRegionId(cmd.requiredReqUserId()).getBody();

    if (Objects.equals(channelEnum, PlatformChannelEnum.GOOGLE)
        && !Objects.equals(cmd.requireReqSysOriginChildEnum(), SysOriginPlatformEnum.YOLO)) {
      return appPurchaseProductRepository
          .getProduct(cmd.requireReqSysOriginChildEnum(), channelEnum.name(),
              receipt.getProductId(), regionId);
    }

    if (purchaseProduct.matches(RegexConstant.NUMBER)) {
      return appPurchaseProductRepository.getByIdV2(Long.valueOf(purchaseProduct));
    }

    if (purchaseProduct.startsWith(StringPool.PAY_PACK_START)) {

      String productPackage = purchaseProduct.replace(StringPool.PAY_PACK_START, "");
      return appPurchaseProductRepository
          .getProduct(cmd.requireReqSysOriginChildEnum(), channelEnum.name(),
              productPackage, regionId);

    }
    ResponseAssert.isTrue(ProductErrorCode.NOT_FOUND_PRODUCT,
        StringUtils.isNotBlank(purchaseProduct));
    return null;
  }

  private ExtractInAppPurchaseReceipt extractReceipt(AbstractPurchaseCmd cmd) {
    return factoryForExtractAppPurchaseOrderStrategy
        .getStrategy(cmd.extractStrategyName())
        .doOperation(cmd);
  }

}
