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

import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils;
import com.red.circle.component.pay.PayProperties;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderDetailsV2;
import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderService;
import com.red.circle.order.app.dto.clientobject.pay.PlaceAnOrderResponseCO;
import com.red.circle.order.app.dto.cmd.PayPlaceAnOrderCmd;
import com.red.circle.order.domain.gateway.UserFreightRechargeRecordGateway;
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseProduct;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplication;
import com.red.circle.order.infra.database.rds.entity.pay.PayApplicationCommodity;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountry;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannelDetails;
import com.red.circle.order.infra.database.rds.service.pay.PayApplicationCommodityService;
import com.red.circle.order.infra.database.rds.service.pay.PayApplicationService;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelDetailsService;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryService;
import com.red.circle.order.infra.util.IpUtils;
import com.red.circle.order.inner.asserts.PayErrorCode;
import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseReceiptType;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.order.app.common.PayerMaxProperties;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import static com.red.circle.tool.core.text.StringPool.SYMBOL_COMMA;

/**
 * web 下单处理.
 *
 * @author pengliang on 2021/7/9
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class PayWebPlaceAnOrderCmdExe {

  private final PayProperties payProperties;
  private final PayerMaxProperties payerMaxProperties;
  private final PayCountryService payCountryService;
  private final SysCountryCodeClient sysCountryCodeClient;
  private final PayApplicationService payApplicationService;
  private final PayPlaceAnOrderService payPlaceAnOrderService;
  private final PayApplicationCommodityService payApplicationCommodityService;
  private final PayCountryChannelDetailsService payCountryChannelDetailsService;
  private final UserFreightRechargeRecordGateway userFreightRechargeRecordGateway;

  public PlaceAnOrderResponseCO execute(PayPlaceAnOrderCmd cmd) {
    return payPlaceAnOrderService.doRequest(getPayPlaceAnOrderDetails(cmd));
  }


  private PayPlaceAnOrderDetailsV2 getPayPlaceAnOrderDetails(PayPlaceAnOrderCmd cmd) {
    PayApplication application = payApplicationService.getById(cmd.getApplicationId());
    // 未知的应用
    ResponseAssert.notNull(PayErrorCode.UNKNOWN_APPLICATION, application);
    PayApplicationCommodity commodity = getAppCommodity(cmd);

    PayCountryChannelDetails channelDetails = payCountryChannelDetailsService
        .getSuggestScoreMax(cmd.getPayCountryId(), cmd.getChannelCode(),
            commodity.getAmountUsd());
    // 没有合适的支付方式
    ResponseAssert.notNull(PayErrorCode.NO_SUITABLE_PAYMENT_CHANNEL, channelDetails);
    PayCountry payCountry = payCountryService.getById(cmd.getPayCountryId());
    // 不支持地区
    ResponseAssert.notNull(PayErrorCode.REGION_NOT_SUPPORTED, payCountry);
    SysCountryCodeDTO sysCountryCode = ResponseAssert.requiredSuccess(
        sysCountryCodeClient.getById(payCountry.getCountryId())
    );
    // 不支持地区
    ResponseAssert.notNull(PayErrorCode.REGION_NOT_SUPPORTED, sysCountryCode);
    PayPlaceAnOrderDetailsV2 details = new PayPlaceAnOrderDetailsV2();
    details.setOrderId(IdWorkerUtils.getIdStr());
    details.setReceiptType(InAppPurchaseReceiptType.PAYMENT);
    details.setApplicationId(Objects.toString(application.getId()));
    details.setAppCommodityType(commodity.getType());
    details.setSysOrigin(application.getAppCode());
    details.setAcceptUserId(cmd.getUserId());
    details.setCreateUserId(cmd.getUserId());
    // 获取实时汇率，失败时使用配置的汇率作为备选
    BigDecimal exchangeRate = getRealTimeExchangeRate(payCountry);
    details.setAmount(PayAmountArithmeticUtils.multiplyUpNotPoint(
            commodity.getAmountUsd(),
            exchangeRate
        )
    );
    details.setAmountUsd(commodity.getAmountUsd());
    details.setProducts(List.of(new InAppPurchaseProduct()
            .setId(commodity.getId())
            .setName(commodity.getType())
            .setCode(commodity.getType())
            .setDescribe(getProductDescriptor(commodity))
            .setContent(
                commodity.getObtainGoldsQuantity().stripTrailingZeros().toPlainString())
            .setAmountUsd(commodity.getAmountUsd())
            .setQuantity(1)
        )
    );
    details.setProductDescriptor(getProductDescriptor(commodity));
    details.setChannelDetails(channelDetails);
    details.setCountry(sysCountryCode);
    details.setPayCountry(payCountry);
    details.setRechargeUrl(payProperties.getWebPayRechargePageUrl()
        + "/" + application.getId()
        + "?type=" + commodity.getType());
    details.setSuccessRedirectUrl(payProperties.getWebPayResultPageUrl()
        + "?redirect=" + payProperties.getWebPayRechargePage()
        + "&appId=" + application.getId()
        + "&appStatus=0"
        + "&appType=" + commodity.getType());
    details.setCancelRedirectUrl(details.getRechargeUrl());
    details.setNewVersion(cmd.getNewVersion());
    details.setRequestIp(IpUtils.getIpAddr());

    // 记录充值金额
    rechargeRecord(details);
    return details;
  }

  /**
   * 获取实时汇率，失败时返回配置的汇率
   * @param payCountry 支付国家配置
   * @return 汇率
   */
  private BigDecimal getRealTimeExchangeRate(PayCountry payCountry) {
    // 检查是否启用实时汇率
    if (!Boolean.TRUE.equals(payerMaxProperties.getEnableRealTimeExchangeRate())) {
      log.info("Real-time exchange rate is disabled, using configured rate for country {}", payCountry.getId());
      return payCountry.getUsdExchangeRate();
    }
    
    BigDecimal realTimeRate = null;
    try {
      // 调用实时汇率查询 - 这里假设目标币种是payCountry对应的币种
      // 您可以根据实际需要调整币种参数
      realTimeRate = payPlaceAnOrderService.getRealTimeExchangeRate("USD", payCountry.getCurrency());
    } catch (Exception e) {
      log.warn("Failed to get real-time exchange rate for country {}, will use configured rate as fallback", 
               payCountry.getId(), e);
    }
    
    // 如果实时汇率获取失败，使用配置的汇率作为备选
    BigDecimal exchangeRate = realTimeRate != null ? realTimeRate : payCountry.getUsdExchangeRate();
    
    // 记录使用的汇率来源
    log.info("Using {} exchange rate: {} for country {} amount calculation", 
             realTimeRate != null ? "real-time" : "configured", 
             exchangeRate, payCountry.getId());
    
    return exchangeRate;
  }

  private void rechargeRecord(PayPlaceAnOrderDetailsV2 details) {
    if (Objects.equals(details.getNewVersion(), Boolean.TRUE)) {
      userFreightRechargeRecordGateway.inrNowMonthAmount(
          details.getCreateUserId(),
          details.getAmountUsd()
      );
    }
  }

  private PayApplicationCommodity getAppCommodity(PayPlaceAnOrderCmd cmd) {
    PayApplicationCommodity commodity = payApplicationCommodityService
        .getAppCommodity(cmd.getGoodsId(), cmd.getApplicationId());
    // 没有找到映射信息
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, commodity);
    // 数量必须大于零
    ResponseAssert.isTrue(CommonErrorCode.QUANTITY_MUST_GT_ZERO, ArithmeticUtils
        .gtZero(commodity.getObtainGoldsQuantity()));
    return commodity;
  }

  public String getProductDescriptor(PayApplicationCommodity commodity) {
    if (Objects.toString(commodity.getType(), "").toUpperCase().contains("GOLD")) {
      return commodity.getType();
    }
    // 1USD = 100+10GOLD
    StringBuilder builder = new StringBuilder()
        .append(commodity.getAmountUsd().stripTrailingZeros().toPlainString())
        .append("USD")
        .append(" = ")
        .append(StringUtils.isBlankOrElse(commodity.getContent(), "0"));
    if (StringUtils.isNotBlank(commodity.getAwardContent())) {
      builder
          .append("+")
          .append(commodity.getAwardContent());
    }
    builder.append(commodity.getType());
    return builder.toString();
  }

}
