package com.jeesharp.business.modules.pay.refund.support;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatCredentialSupport;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatWxPayClientHolder;
import com.jeesharp.business.modules.pay.core.IJPayHttpResponse;
import com.jeesharp.business.modules.pay.core.enums.RequestMethod;
import com.jeesharp.business.modules.pay.paychannelconfig.support.PayChannelTenantSupport;
import com.jeesharp.business.modules.pay.paywechatconfig.entity.PayWechatConfig;
import com.jeesharp.business.modules.pay.paywechatconfig.enums.WechatPayModeEnum;
import com.jeesharp.business.modules.pay.refund.dto.PayRefundRequestDTO;
import com.jeesharp.business.modules.pay.refund.dto.PayRefundResultDTO;
import com.jeesharp.business.modules.pay.wxpay.WxPayApi;
import com.jeesharp.business.modules.pay.wxpay.enums.WxApiType;
import com.jeesharp.business.modules.pay.wxpay.enums.WxDomain;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * 微信 V3 退款客户端：按 {@code wechatPayMode} 分流境内直连与电商收付通。
 *
 * @author jeesharp
 * @since 2026-06-11
 */
@Component
@RequiredArgsConstructor
public class PayWechatRefundClient {

    private final PayWechatWxPayClientHolder payWechatWxPayClientHolder;

    public PayRefundResultDTO applyRefund(PayRefundRequestDTO request) {
        if (isPartnerMode(request.getWechatPayMode())) {
            return applyPartnerRefund(request);
        }
        return applyDirectRefund(request);
    }

    public PayRefundResultDTO queryByOutRefundNo(String platformTenantId, String wechatPayMode,
            String subMchId, String outRefundNo) {
        if (isPartnerMode(wechatPayMode)) {
            return queryPartnerByOutRefundNo(platformTenantId, subMchId, outRefundNo);
        }
        return queryDirectByOutRefundNo(platformTenantId, outRefundNo);
    }

    private PayRefundResultDTO applyDirectRefund(PayRefundRequestDTO request) {
        PayRefundResultDTO result = new PayRefundResultDTO();
        try {
            PayWechatConfig config = resolveConfig(request.getPlatformTenantId());
            JSONObject body = buildDirectApplyBody(request);
            IJPayHttpResponse response = v3Post(config, WxApiType.DIRECT_REFUNDS.getType(), body.toString());
            return toResult(parseResponse(response, "申请退款失败"), body.toString());
        } catch (Exception ex) {
            result.setSuccess(false);
            result.setErrorMsg(ex.getMessage());
            return result;
        }
    }

    private PayRefundResultDTO applyPartnerRefund(PayRefundRequestDTO request) {
        PayRefundResultDTO result = new PayRefundResultDTO();
        try {
            if (StrUtil.isBlank(request.getSubMchId())) {
                throw new IllegalStateException("收付通退款缺少 sub_mchid");
            }
            PayWechatConfig config = resolveConfig(request.getPlatformTenantId());
            JSONObject body = buildPartnerApplyBody(config, request);
            IJPayHttpResponse response = v3Post(config, WxApiType.E_COMMERCE_REFUNDS.getType(), body.toString());
            return toResult(parseResponse(response, "申请退款失败"), body.toString());
        } catch (Exception ex) {
            result.setSuccess(false);
            result.setErrorMsg(ex.getMessage());
            return result;
        }
    }

    private PayRefundResultDTO queryDirectByOutRefundNo(String platformTenantId, String outRefundNo) {
        PayRefundResultDTO result = new PayRefundResultDTO();
        try {
            PayWechatConfig config = resolveConfig(platformTenantId);
            String suffix = String.format(WxApiType.DIRECT_QUERY_REFUNDS_BY_REFUND_NO.getType(), outRefundNo);
            IJPayHttpResponse response = v3Get(config, suffix);
            return toResult(parseResponse(response, "查询退款失败"), null);
        } catch (Exception ex) {
            result.setSuccess(false);
            result.setErrorMsg(ex.getMessage());
            return result;
        }
    }

    private PayRefundResultDTO queryPartnerByOutRefundNo(String platformTenantId, String subMchId, String outRefundNo) {
        PayRefundResultDTO result = new PayRefundResultDTO();
        try {
            if (StrUtil.isBlank(subMchId)) {
                throw new IllegalStateException("收付通退款查询缺少 sub_mchid");
            }
            PayWechatConfig config = resolveConfig(platformTenantId);
            String suffix = String.format(WxApiType.QUERY_REFUNDS_BY_REFUND_NO.getType(), outRefundNo)
                    + "?sub_mchid=" + subMchId;
            IJPayHttpResponse response = v3Get(config, suffix);
            return toResult(parseResponse(response, "查询退款失败"), null);
        } catch (Exception ex) {
            result.setSuccess(false);
            result.setErrorMsg(ex.getMessage());
            return result;
        }
    }

    private PayWechatConfig resolveConfig(String platformTenantId) {
        String tenantId = StrUtil.blankToDefault(platformTenantId, PayChannelTenantSupport.PLATFORM_TENANT_ID);
        PayWechatConfig config = payWechatWxPayClientHolder.requireActiveConfig(tenantId);
        if (!PayWechatCredentialSupport.isWxEnabled(config)) {
            throw new IllegalStateException("未启用真实微信 V3：请在后台开启「调用真实微信」");
        }
        return config;
    }

    private static boolean isPartnerMode(String wechatPayMode) {
        return WechatPayModeEnum.PARTNER == WechatPayModeEnum.fromValue(wechatPayMode);
    }

    private static JSONObject buildDirectApplyBody(PayRefundRequestDTO request) {
        JSONObject body = new JSONObject();
        if (StrUtil.isNotBlank(request.getTransactionId())) {
            body.set("transaction_id", request.getTransactionId());
        }
        if (StrUtil.isNotBlank(request.getOutTradeNo())) {
            body.set("out_trade_no", request.getOutTradeNo());
        }
        body.set("out_refund_no", request.getOutRefundNo());
        body.set("reason", StrUtil.blankToDefault(request.getReason(), "用户申请退款"));
        if (StrUtil.isNotBlank(request.getNotifyUrl())) {
            body.set("notify_url", request.getNotifyUrl());
        }
        body.set("amount", buildAmount(request));
        return body;
    }

    private static JSONObject buildPartnerApplyBody(PayWechatConfig config, PayRefundRequestDTO request) {
        JSONObject body = new JSONObject();
        body.set("sp_appid", config.getAppId());
        body.set("sub_mchid", request.getSubMchId());
        if (StrUtil.isNotBlank(request.getTransactionId())) {
            body.set("transaction_id", request.getTransactionId());
        }
        if (StrUtil.isNotBlank(request.getOutTradeNo())) {
            body.set("out_trade_no", request.getOutTradeNo());
        }
        body.set("out_refund_no", request.getOutRefundNo());
        body.set("reason", StrUtil.blankToDefault(request.getReason(), "用户申请退款"));
        if (StrUtil.isNotBlank(request.getNotifyUrl())) {
            body.set("notify_url", request.getNotifyUrl());
        }
        body.set("amount", buildAmount(request));
        return body;
    }

    private static JSONObject buildAmount(PayRefundRequestDTO request) {
        JSONObject amount = new JSONObject();
        amount.set("refund", request.getRefundAmountFen());
        amount.set("total", request.getOrderAmountFen());
        amount.set("currency", "CNY");
        return amount;
    }

    private static PayRefundResultDTO toResult(JSONObject wxResp, String requestBody) {
        PayRefundResultDTO result = new PayRefundResultDTO();
        result.setWxRefundId(wxResp.getStr("refund_id"));
        result.setStatus(wxResp.getStr("status"));
        if (StringUtils.hasText(requestBody)) {
            result.setRawResponse(JSONUtil.createObj()
                    .set("request", requestBody)
                    .set("response", wxResp)
                    .toString());
        } else {
            result.setRawResponse(wxResp.toString());
        }
        result.setSuccess(StrUtil.isNotBlank(result.getWxRefundId())
                || "PROCESSING".equalsIgnoreCase(result.getStatus())
                || "SUCCESS".equalsIgnoreCase(result.getStatus()));
        return result;
    }

    private IJPayHttpResponse v3Post(PayWechatConfig config, String urlSuffix, String body) throws Exception {
        return WxPayApi.v3(RequestMethod.POST, WxDomain.CHINA.getType(), urlSuffix,
                config.getMchId(),
                PayWechatCredentialSupport.resolveMerchantSerialNo(config),
                config.getPublicKeyId(),
                config.getCertPrivateKeyPath().trim(),
                body);
    }

    private IJPayHttpResponse v3Get(PayWechatConfig config, String urlSuffix) throws Exception {
        return WxPayApi.v3(RequestMethod.GET, WxDomain.CHINA.getType(), urlSuffix,
                config.getMchId(),
                PayWechatCredentialSupport.resolveMerchantSerialNo(config),
                config.getPublicKeyId(),
                config.getCertPrivateKeyPath().trim(),
                "");
    }

    private static JSONObject parseResponse(IJPayHttpResponse response, String action) {
        if (response == null) {
            throw new IllegalStateException(action + "：无响应");
        }
        int status = response.getStatus();
        String body = response.getBody();
        if (status >= 200 && status < 300) {
            return StringUtils.hasText(body) ? JSONUtil.parseObj(body) : new JSONObject();
        }
        String msg = body;
        if (StringUtils.hasText(body) && JSONUtil.isTypeJSON(body)) {
            JSONObject err = JSONUtil.parseObj(body);
            msg = err.getStr("message", body);
            if (err.containsKey("code")) {
                msg = err.getStr("code") + ": " + msg;
            }
        }
        throw new IllegalStateException(action + "（HTTP " + status + "）：" + msg);
    }
}
