package com.jeesharp.business.pay.service;

import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.finance.api.PayOrderApi;
import com.jeesharp.business.modules.finance.payorder.dto.PayOrderPrepayRouteDTO;
import com.jeesharp.business.modules.finance.payorder.vo.PayOrderIntentVO;
import com.jeesharp.business.modules.member.application.MemberApplicationService;
import com.jeesharp.business.modules.pay.api.PayCenterApi;
import com.jeesharp.business.modules.pay.dto.BasicPayOrderData;
import com.jeesharp.business.modules.pay.dto.PayWechatPrepayRouteSnapshotDTO;
import com.jeesharp.business.modules.pay.dto.PayWechatSubPayItemDTO;
import com.jeesharp.business.modules.pay.enums.PayTypeStyle;
import com.jeesharp.business.modules.pay.support.WechatPayAttachSupport;
import com.jeesharp.business.modules.pay.vo.WechatJsapiPrepayVO;
import com.jeesharp.business.pay.model.WechatJsapiPrepayResult;
import com.jeesharp.business.pay.model.WechatPrepayCommand;
import com.jeesharp.business.pay.model.WechatPrepayOutcome;
import com.jeesharp.business.pay.model.WechatSubPayItem;
import com.jeesharp.business.pay.support.WechatJsapiPrepayVoMapper;
import com.jeesharp.business.pay.support.WechatTravelPayNotifySupport;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 网关内统一编排：创建财务支付单、经 {@link PayCenterApi} 调用微信 V3、回写 prepay_id，并向各业务预支付 VO 回填调起参数。
 *
 * <p>主单仅写入 {@code biz_finance_pay_order}；渠道能力由支付中心 {@link PayCenterApi} 提供（配置来自后台 pay_wechat_config）。</p>
 *
 * @author jeesharp
 * @since 2026-05-08
 */
@Service
@AllArgsConstructor
public class WechatJsapiPayOrderPrepayService {

    private final PayOrderApi payOrderApi;
    private final PayCenterApi payCenterApi;
    private final MemberApplicationService memberApplicationService;

    /**
     * 创建支付单并请求微信 JSAPI 预支付，返回调起收银台所需数据。
     */
    public WechatPrepayOutcome execute(WechatPrepayCommand cmd) throws Exception {
        BigDecimal amountYuan = BigDecimal.valueOf(cmd.getAmountFen()).movePointLeft(2);
        String memberId = StrUtil.nullToDefault(cmd.getMemberId(), "");
        String openid = memberApplicationService.getWechatMiniOpenidByMemberIdAppId(memberId, cmd.getAppId());
        String notifyBase = payCenterApi.getWechatPublicConfig(cmd.getTenantId()).getNotifyBaseUrl();
        if (StrUtil.isBlank(notifyBase)) {
            throw new IllegalStateException("未配置微信支付回调基地址，请在管理后台【支付 · 微信配置】填写 notifyBaseUrl");
        }
        String notifyUrl = WechatTravelPayNotifySupport.buildNotifyUrl(notifyBase);

        BasicPayOrderData previewData = buildPayOrderData(cmd, null, null, notifyUrl, openid, memberId);
        PayWechatPrepayRouteSnapshotDTO routeSnapshot = payCenterApi.previewWechatJsapiRoute(previewData);
        PayOrderPrepayRouteDTO routeDto = toPayOrderRoute(routeSnapshot);

        PayOrderIntentVO intent = payOrderApi.createIntentForJsapi(
                cmd.getBizScene(),
                cmd.getBizOrderId(),
                amountYuan,
                StrUtil.isBlank(memberId) ? null : memberId,
                cmd.getDescription(),
                cmd.getDescription(),
                notifyUrl,
                openid,
                null,
                cmd.getTenantId(),
                cmd.getSettleMode(),
                cmd.getNeedProfitSharing(),
                routeDto);

        if ("COMBINE".equals(routeDto.getPayRouteType())) {
            routeDto.setCombineOutTradeNo(intent.getOutTradeNo());
        }

        BasicPayOrderData payData = buildPayOrderData(cmd, intent.getPayOrderId(), intent.getOutTradeNo(), notifyUrl,
                openid, memberId);
        WechatJsapiPrepayVO prepayVo = payCenterApi.wechatJsapiPrepay(payData);
        WechatJsapiPrepayResult payResult = WechatJsapiPrepayVoMapper.toGatewayResult(prepayVo);

        payOrderApi.bindPrepayAfterJsapi(intent.getPayOrderId(), resolvePrepayId(payResult));
        return new WechatPrepayOutcome(intent.getOutTradeNo(), intent.getPayOrderId(), payResult, openid);
    }

    public void applyPrepare(Object vo, WechatPrepayOutcome outcome) {
        WechatJsapiPrepayVoMapper.applyPrepareOutcome(vo, outcome);
    }

    private static BasicPayOrderData buildPayOrderData(WechatPrepayCommand cmd, String financePayOrderId,
            String outTradeNo, String notifyUrl, String openid, String memberId) {
        BasicPayOrderData payData = new BasicPayOrderData();
        payData.setPayType(PayTypeStyle.WX_PAY.getValue());
        payData.setBizScene(cmd.getBizScene());
        payData.setBizOrderId(cmd.getBizOrderId());
        payData.setFinancePayOrderId(financePayOrderId);
        payData.setUserId(memberId);
        payData.setOrderNumber(outTradeNo);
        payData.setSubject(cmd.getDescription());
        payData.setDescription(cmd.getDescription());
        payData.setNotifyUrl(notifyUrl);
        payData.setAttach(WechatPayAttachSupport.buildTravelAttach(
                WechatPayAttachSupport.resolveAttachBizType(cmd.getBizScene()),
                cmd.getBizOrderId(),
                financePayOrderId,
                memberId));
        payData.setPayAmount(cmd.getAmountFen());
        payData.setPayerOpenId(openid);
        payData.setWxTradeType("JSAPI");
        payData.setTenantId(cmd.getTenantId());
        payData.setSettleMode(cmd.getSettleMode());
        payData.setNeedProfitSharing(cmd.getNeedProfitSharing());
        payData.setSubPayItems(mapSubPayItems(cmd.getSubPayItems()));
        return payData;
    }

    private static List<PayWechatSubPayItemDTO> mapSubPayItems(List<WechatSubPayItem> items) {
        if (items == null || items.isEmpty()) {
            return List.of();
        }
        List<PayWechatSubPayItemDTO> result = new ArrayList<>();
        for (WechatSubPayItem item : items) {
            if (item == null || StrUtil.isBlank(item.getMerchantTenantId())) {
                continue;
            }
            PayWechatSubPayItemDTO dto = new PayWechatSubPayItemDTO();
            dto.setMerchantTenantId(item.getMerchantTenantId());
            dto.setBizOrderId(item.getBizOrderId());
            dto.setAmountFen(item.getAmountFen());
            dto.setNeedProfitSharing(item.getNeedProfitSharing());
            result.add(dto);
        }
        return result;
    }

    private static PayOrderPrepayRouteDTO toPayOrderRoute(PayWechatPrepayRouteSnapshotDTO snapshot) {
        PayOrderPrepayRouteDTO dto = new PayOrderPrepayRouteDTO();
        if (snapshot == null) {
            return dto;
        }
        dto.setWechatPayMode(snapshot.getWechatPayMode());
        dto.setPayRouteType(snapshot.getPayRouteType());
        dto.setSubMchId(snapshot.getSubMchId());
        dto.setCombineOutTradeNo(snapshot.getCombineOutTradeNo());
        return dto;
    }

    private static String resolvePrepayId(WechatJsapiPrepayResult payResult) {
        if (payResult == null) {
            return null;
        }
        if (payResult.getPrepayId() != null && !payResult.getPrepayId().isBlank()) {
            return payResult.getPrepayId();
        }
        String pkg = payResult.getPackageValue();
        if (pkg != null && pkg.startsWith("prepay_id=")) {
            return pkg.substring("prepay_id=".length()).trim();
        }
        return null;
    }
}
